21. The G430 Interface

The work mentioned here details code related to interfacing with the G430 drawing tablet interface. This has been a tablet I have been interested in for a while due to its small form factor, as well as its passive stylus connection. It also doesn't hurt that the tablet seems to display 15-bits of resolution (whether or not these are oversampled is a question).

This interface is what I would call a "secondary" interface, meaning it is not implemented inside of a page callback. Instead, pages wishing to use this must explictly set a callback to control the device when the page is selected.

21.1. DONE Virtual Interface

CLOSED: [2019-08-21 Wed 16:57]

21.1.1. DONE Top Level Declaration

CLOSED: [2019-08-20 Tue 18:06]

<<struct_contents>>=
monolith_g430_d vg430;
<<init>>=
monolith_g430_init(m, &m->vg430);

21.1.2. DONE Struct

CLOSED: [2019-08-20 Tue 18:05]

21.1.2.1. Typedef

<<typedefs>>=
typedef struct monolith_g430_d monolith_g430_d;
<<structs>>=
struct monolith_g430_d {
<<g430_struct_contents>>
};
21.1.2.2. Contents

21.1.2.2.1. User Data

User data, passed to the callback. By default, initialized to be empty.

<<g430_struct_contents>>=
void *ud;
<<g430_init>>=
g->ud = NULL;

Set it with this callback:

<<function_declarations>>=
void monolith_g430_ud_set(monolith_g430_d *g, void *ud);
<<functions>>=
void monolith_g430_ud_set(monolith_g430_d *g, void *ud)
{
    g->ud = ud;
}
21.1.2.2.2. Callback

The callback structure happens anytime a new event happens. The following arguments are given here:

- monolith data - state (on/off + buttons) - x axis (normalized) - y axis (normalized) - z axis (normalized) - user data

<<g430_struct_contents>>=
void (*cb)(monolith_d*,int,GFFLT,GFFLT,GFFLT,void*);
<<g430_init>>=
g->cb = NULL;

This can be set using the function monolith_g430_cb_set.

<<function_declarations>>=
void monolith_g430_cb_set(monolith_g430_d *g,
                          void (*cb)(monolith_d*,
                                      int,
                                      GFFLT,
                                      GFFLT,
                                      GFFLT,
                                      void*));
<<functions>>=
void monolith_g430_cb_set(monolith_g430_d *g,
                          void (*cb) (monolith_d*,
                                      int,
                                      GFFLT,
                                      GFFLT,
                                      GFFLT,
                                      void*))
{
    g->cb = cb;
}
21.1.2.2.3. HID handle

This is the handle used by the hidapi. By default, it is empty.

<<g430_struct_contents>>=
#ifndef MONOLITH_SIMPLE
hid_device *handle;
#endif
<<g430_init>>=
#ifndef MONOLITH_SIMPLE
g->handle = NULL;
#endif

21.1.3. DONE Event Callback

CLOSED: [2019-08-20 Tue 18:05] The event callback is called using the function. monolith_g430_evt

<<function_declarations>>=
void monolith_g430_evt(monolith_d *m,
                       int state,
                       GFFLT x,
                       GFFLT y,
                       GFFLT z);
<<functions>>=
void monolith_g430_evt(monolith_d *m,
                       int state,
                       GFFLT x,
                       GFFLT y,
                       GFFLT z)
{
#ifndef MONOLITH_SIMPLE
    monolith_g430_d *g;

    g = &m->vg430;
    if(g->cb == NULL) return;
    g->cb(m, state, x, y, z, g->ud);
#endif
}

21.1.4. DONE Initialization

CLOSED: [2019-08-20 Tue 17:55]

<<function_declarations>>=
void monolith_g430_init(monolith_d *m, monolith_g430_d *g);
<<functions>>=
void monolith_g430_init(monolith_d *m, monolith_g430_d *g)
{
<<g430_init>>
}

21.2. DONE Hardware Polling

CLOSED: [2019-08-21 Wed 16:57]

21.2.1. DONE Setup and Cleanup

CLOSED: [2019-08-20 Tue 18:24] The actual hardware is setup to be polled with monolith_g430_setup. We open the handle using the hidapi interface.

<<function_declarations>>=
void monolith_g430_setup(monolith_d *m);
<<functions>>=
void monolith_g430_setup(monolith_d *m)
{
#ifndef MONOLITH_SIMPLE
    monolith_g430_d *g;
    g = &m->vg430;
    if(g->handle != NULL) return;

    /* Vendor + Product ID */
    g->handle = hid_open(0x28bd, 0x0075, NULL);

    if(g->handle == NULL) {
        fprintf(stderr, "Could not open G430\n");
        return;
    }

    hid_set_nonblocking(g->handle, 1);

    monolith_g430_defaults(g);
#endif
}

Cleanup is done with monolith_g430_cleanup.

<<function_declarations>>=
void monolith_g430_cleanup(monolith_d *m);
<<functions>>=
void monolith_g430_cleanup(monolith_d *m)
{
#ifndef MONOLITH_SIMPLE
    monolith_g430_d *g;

    g = &m->vg430;

    if(g->handle != NULL) {
        hid_close(g->handle);
    }
#endif
}

This gets called at the end of the program.

<<cleanup>>=
monolith_g430_cleanup(m);

21.2.2. DONE Polling

CLOSED: [2019-08-20 Tue 18:55] Polling is done with the function monolith_g430_poll.

<<function_declarations>>=
void monolith_g430_poll(monolith_d *m);

In this polling function, XY values are scaled to match the 4:3 ratio of the surface. This is done because the hardware (on Linux) causes the values to be normalize 15 bit values.


<<functions>>=
void monolith_g430_poll(monolith_d *m)
{
#ifndef MONOLITH_SIMPLE
    static unsigned char buf[8];
    uint16_t x, y, p;
    GFFLT x_s, y_s, p_s;
    int res;
    monolith_g430_d *g;

    g = &m->vg430;

    if(g->handle == NULL) return;

    res = hid_read(g->handle, buf, 8);

    if (res < 0)
        printf("Unable to read()\n");

    if(res > 0) {
        x = buf[2] + (buf[3] << 8); /* 20320 max maybe? */
        y = buf[4] + (buf[5] << 8); /* 15240 max maybe? */
        p = buf[6] + (buf[7] << 8); /* 8191 probably */

        /* normalize + scale */
        x_s = (GFFLT) x / 32767;
        y_s = (GFFLT) y / 32767;
        p_s = (GFFLT) p / 8191;

        monolith_g430_evt(m, buf[1], x_s, y_s, p_s);
    }
#endif
}

It gets polled in some inner loop, define below.

<<poll_the_g430>>=
monolith_g430_poll(m);

21.2.3. DONE Scheme Functions

CLOSED: [2019-08-20 Tue 18:55] You start it in scheme with monolith:g430-setup.

<<primitive_entries>>=
{"monolith:g430-setup", pp_g430_setup, 0, 0, {___,___,___}},
<<scheme_functions>>=
static cell pp_g430_setup(cell x) {
    monolith_g430_setup(monolith_data_get());
    return UNSPECIFIC;
}

21.3. DONE Default Behavior + Nodes

CLOSED: [2019-08-26 Mon 14:06] This behavior stores x, y, z values to be used inside of some graforge nodes.

21.3.1. Set Default

<<function_declarations>>=
void monolith_g430_defaults(monolith_g430_d *g);
<<functions>>=
<<g430_default_callback>>
void monolith_g430_defaults(monolith_g430_d *g)
{
    monolith_g430_cb_set(g, g430_default);
    monolith_g430_ud_set(g, g);
}

21.3.2. Data

Floating point values, as well as state data, are stored inside the g430 struct

<<g430_struct_contents>>=
GFFLT x, y, z, s;
<<g430_init>>=
g->x = 0;
g->y = 0;
g->z = 0;
g->s = 0;

21.3.3. Callback

The callback in question is called g430_default.

<<g430_default_callback>>=
static void g430_default(monolith_d *m,
                         int s,
                         GFFLT x,
                         GFFLT y,
                         GFFLT z,
                         void *ud)
{
    monolith_g430_d *g;

    g = ud;
    g->x = x;
    g->y = y;
    g->z = z;
    g->s = s;
}

21.3.4. DONE Nodes

CLOSED: [2019-08-22 Thu 15:04]

21.3.4.1. g430_val

All that is needed here is one node type, used to send the default stored values to the output.

21.3.4.1.1. Init

<<static_function_declarations>>=
static void node_g430_val(gf_node *node, GFFLT *val);
<<functions>>=
<<g430_val_callbacks>>
static void node_g430_val(gf_node *node, GFFLT *val)
{
    gf_node_cables_alloc(node, 1);
    gf_node_set_block(node, 0);
    gf_node_set_compute(node, g430_val_compute);
    gf_node_set_destroy(node, g430_val_destroy);
    gf_node_set_data(node, val);
}
21.3.4.1.2. Compute

<<g430_val_callbacks>>=
static void g430_val_compute(gf_node *n)
{
    gf_cable *out;
    int blksize;
    GFFLT *val;
    int s;

    blksize = gf_node_blksize(n);
    gf_node_get_cable(n, 0, &out);
    val = gf_node_get_data(n);

    for(s = 0; s < blksize; s++) {
        gf_cable_set(out, s, *val);
    }
}
21.3.4.1.3. Destroy

<<g430_val_callbacks>>=
static void g430_val_destroy(gf_node *n)
{
    gf_node_cables_free(n);
}

21.3.5. DONE Runt Words

CLOSED: [2019-08-26 Mon 14:06]

21.3.5.1. g430x

<<monolith_runt_loader>>=
monolith_runt_define(m, "g430x", 5, rproc_g430x);
<<static_function_declarations>>=
static runt_int rproc_g430x(runt_vm *vm, runt_ptr p);
<<functions>>=
static runt_int rproc_g430x(runt_vm *vm, runt_ptr p)
{
    monolith_g430_d *g;
    monolith_d *m;
    runt_int rc;
    gf_patch *patch;
    gf_node *node;
    runt_stacklet *out;

    rc = runt_ppush(vm, &out);
    RUNT_ERROR_CHECK(rc);

    m = runt_to_cptr(p);
    patch = monolith_graforge_get(m);
    rc = gf_patch_new_node(patch, &node);
    GF_RUNT_ERROR_CHECK(rc);

    g = &m->vg430;

    node_g430_val(node, &g->x);

    rgf_push_output(vm, node, out, 0);
    return RUNT_OK;
}
21.3.5.2. DONE g430y

CLOSED: [2019-08-22 Thu 15:08]

<<monolith_runt_loader>>=
monolith_runt_define(m, "g430y", 5, rproc_g430y);
<<static_function_declarations>>=
static runt_int rproc_g430y(runt_vm *vm, runt_ptr p);
<<functions>>=
static runt_int rproc_g430y(runt_vm *vm, runt_ptr p)
{
    monolith_g430_d *g;
    monolith_d *m;
    runt_int rc;
    gf_patch *patch;
    gf_node *node;
    runt_stacklet *out;

    rc = runt_ppush(vm, &out);
    RUNT_ERROR_CHECK(rc);

    m = runt_to_cptr(p);
    patch = monolith_graforge_get(m);
    rc = gf_patch_new_node(patch, &node);
    GF_RUNT_ERROR_CHECK(rc);

    g = &m->vg430;

    node_g430_val(node, &g->y);

    rgf_push_output(vm, node, out, 0);
    return RUNT_OK;
}
21.3.5.3. DONE g430z

CLOSED: [2019-08-22 Thu 15:09]

<<monolith_runt_loader>>=
monolith_runt_define(m, "g430z", 5, rproc_g430z);
<<static_function_declarations>>=
static runt_int rproc_g430z(runt_vm *vm, runt_ptr p);
<<functions>>=
static runt_int rproc_g430z(runt_vm *vm, runt_ptr p)
{
    monolith_g430_d *g;
    monolith_d *m;
    runt_int rc;
    gf_patch *patch;
    gf_node *node;
    runt_stacklet *out;

    rc = runt_ppush(vm, &out);
    RUNT_ERROR_CHECK(rc);

    m = runt_to_cptr(p);
    patch = monolith_graforge_get(m);
    rc = gf_patch_new_node(patch, &node);
    GF_RUNT_ERROR_CHECK(rc);

    g = &m->vg430;

    node_g430_val(node, &g->z);

    rgf_push_output(vm, node, out, 0);
    return RUNT_OK;
}
21.3.5.4. DONE g430s

CLOSED: [2019-08-26 Mon 14:06]

<<monolith_runt_loader>>=
monolith_runt_define(m, "g430s", 5, rproc_g430s);
<<static_function_declarations>>=
static runt_int rproc_g430s(runt_vm *vm, runt_ptr p);
<<functions>>=
static runt_int rproc_g430s(runt_vm *vm, runt_ptr p)
{
    monolith_g430_d *g;
    monolith_d *m;
    runt_int rc;
    gf_patch *patch;
    gf_node *node;
    runt_stacklet *out;

    rc = runt_ppush(vm, &out);
    RUNT_ERROR_CHECK(rc);

    m = runt_to_cptr(p);
    patch = monolith_graforge_get(m);
    rc = gf_patch_new_node(patch, &node);
    GF_RUNT_ERROR_CHECK(rc);

    g = &m->vg430;

    node_g430_val(node, &g->s);

    rgf_push_output(vm, node, out, 0);
    return RUNT_OK;
}



prev | home | next