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]
monolith_g430_d vg430;
monolith_g430_init(m, &m->vg430);
21.1.2. DONE Struct
CLOSED: [2019-08-20 Tue 18:05]
21.1.2.1. Typedef
typedef struct monolith_g430_d monolith_g430_d;
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.
void *ud;
g->ud = NULL;
Set it with this callback:
void monolith_g430_ud_set(monolith_g430_d *g, void *ud);
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
void (*cb)(monolith_d*,int,GFFLT,GFFLT,GFFLT,void*);
g->cb = NULL;
This can be set using the function monolith_g430_cb_set
.
void monolith_g430_cb_set(monolith_g430_d *g,
void (*cb)(monolith_d*,
int,
GFFLT,
GFFLT,
GFFLT,
void*));
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.
#ifndef MONOLITH_SIMPLE
hid_device *handle;
#endif
#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
void monolith_g430_evt(monolith_d *m,
int state,
GFFLT x,
GFFLT y,
GFFLT z);
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]
void monolith_g430_init(monolith_d *m, monolith_g430_d *g);
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.
void monolith_g430_setup(monolith_d *m);
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
.
void monolith_g430_cleanup(monolith_d *m);
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.
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
.
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.
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.
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
.
{"monolith:g430-setup", pp_g430_setup, 0, 0, {___,___,___}},
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
void monolith_g430_defaults(monolith_g430_d *g);
<<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
GFFLT x, y, z, s;
g->x = 0;
g->y = 0;
g->z = 0;
g->s = 0;
21.3.3. Callback
The callback in question is called g430_default
.
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 void node_g430_val(gf_node *node, GFFLT *val);
<<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
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
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_define(m, "g430x", 5, rproc_g430x);
static runt_int rproc_g430x(runt_vm *vm, runt_ptr p);
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_define(m, "g430y", 5, rproc_g430y);
static runt_int rproc_g430y(runt_vm *vm, runt_ptr p);
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_define(m, "g430z", 5, rproc_g430z);
static runt_int rproc_g430z(runt_vm *vm, runt_ptr p);
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_define(m, "g430s", 5, rproc_g430s);
static runt_int rproc_g430s(runt_vm *vm, runt_ptr p);
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