summaryrefslogtreecommitdiff
path: root/tests/x11/test_tablet.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/x11/test_tablet.c')
-rw-r--r--tests/x11/test_tablet.c210
1 files changed, 148 insertions, 62 deletions
diff --git a/tests/x11/test_tablet.c b/tests/x11/test_tablet.c
index 243ae00..be5da4c 100644
--- a/tests/x11/test_tablet.c
+++ b/tests/x11/test_tablet.c
@@ -1,10 +1,18 @@
/* Exercise native XI2 samples through the backend and public input queue. */
#include "../../src/platform/x11/window.c"
-static struct yasl_event next(struct x11_surface *surface)
+static struct yasl_private_event next(struct x11_surface *surface)
{
- struct yasl_event event;
- assert(yasl_surface_poll_event(&surface->base, &event));
+ struct yasl_private_event event;
+ /* This backend unit harness owns a standalone input queue, without a
+ * registered display event FIFO. Read rich private events directly. */
+ static unsigned count;
+ count++;
+ if (!input_queue_pop(&surface->input, &event)) {
+ fprintf(stderr, "missing private event at expectation %u (queued=%zu reset=%d)\n",
+ count, surface->input.count, surface->input.reset_pending);
+ abort();
+ }
assert(event.surface == &surface->base);
return event;
}
@@ -13,51 +21,51 @@ struct drag_test_recorder {
int over_count;
int leave_count;
int drop_count;
- struct yasl_vector2i last_pos;
+ struct yasl_private_vector2i last_pos;
};
-static void on_test_drag(struct yasl_surface *s, enum yasl_drag_event ev,
- struct yasl_vector2i pos, void *ud)
+static void on_test_drag(struct yasl_surface *s, enum yasl_private_drag_event ev,
+ struct yasl_private_vector2i pos, void *ud)
{
(void)s;
struct drag_test_recorder *rec = ud;
rec->last_pos = pos;
- if (ev == YASL_DRAG_OVER) rec->over_count++;
- else if (ev == YASL_DRAG_LEAVE) rec->leave_count++;
- else if (ev == YASL_DRAG_DROP) rec->drop_count++;
+ if (ev == YASL_PRIVATE_DRAG_OVER) rec->over_count++;
+ else if (ev == YASL_PRIVATE_DRAG_LEAVE) rec->leave_count++;
+ else if (ev == YASL_PRIVATE_DRAG_DROP) rec->drop_count++;
}
struct drop_test_recorder {
int over_count;
int leave_count;
- struct yasl_vector2i last_pos;
+ struct yasl_private_vector2i last_pos;
};
-static void on_test_drop(struct yasl_surface *s, enum yasl_drop_event ev,
- struct yasl_vector2i pos, void *ud)
+static void on_test_drop(struct yasl_surface *s, enum yasl_private_drop_event ev,
+ struct yasl_private_vector2i pos, void *ud)
{
(void)s;
struct drop_test_recorder *rec = ud;
rec->last_pos = pos;
- if (ev == YASL_DROP_OVER) rec->over_count++;
- else if (ev == YASL_DROP_LEAVE) rec->leave_count++;
+ if (ev == YASL_PRIVATE_DROP_OVER) rec->over_count++;
+ else if (ev == YASL_PRIVATE_DROP_LEAVE) rec->leave_count++;
}
-static void on_test_drag_end(void *ud, enum yasl_drag_end how)
+static void on_test_drag_end(void *ud, enum yasl_private_drag_end how)
{
- enum yasl_drag_end *res = ud;
+ enum yasl_private_drag_end *res = ud;
*res = how;
}
static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
{
/* 1. Caps */
- assert(caps(&d->base) & YASL_CAP_POPUPS);
- assert(caps(&d->base) & YASL_CAP_DRAG);
- assert(caps(&d->base) & YASL_CAP_DRAG_CARRY);
+ assert(caps(&d->base) & YASL_PRIVATE_CAP_POPUPS);
+ assert(caps(&d->base) & YASL_PRIVATE_CAP_DRAG);
+ assert(caps(&d->base) & YASL_PRIVATE_CAP_DRAG_CARRY);
/* 2. Popups: spec validation, calculation, creation, reposition, destruction */
- struct yasl_popup_spec invalid_specs[] = {
+ struct yasl_private_popup_spec invalid_specs[] = {
{.size = {0, 100}},
{.size = {100, 0}},
{.size = {35000, 100}},
@@ -70,7 +78,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(!surface_create_popup(&s->base, &invalid_specs[i]));
}
- struct yasl_popup_spec valid_spec = {
+ struct yasl_private_popup_spec valid_spec = {
.size = {100, 120},
.anchor = {30, 40, 50, 60},
.beside = false,
@@ -78,12 +86,40 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
};
assert(popup_spec_valid(s, &valid_spec));
int x = 0, y = 0;
- popup_calculate_position(d, s, &valid_spec, &x, &y);
+ struct yasl_private_vector2i actual_size;
+ popup_calculate_position(d, s, &valid_spec, &x, &y, &actual_size);
assert(x == 30 - 5);
assert(y == 40 + 60 - 10);
+ /* Explicit XDG-style anchor/gravity stays exact unless a constraint allows a fit. */
+ struct yasl_private_popup_spec positioned = {
+ .positioner_explicit = true,
+ .anchor_edges = 2, /* bottom */
+ .gravity = 2, /* below */
+ .size = {100, 120}, .anchor = {100, 100, 40, 20},
+ .content_offset = {5, 10},
+ };
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x == 68 && y == 110 && actual_size.x == 100 && actual_size.y == 120);
+ positioned.anchor_edges = 4; /* right edge */
+ positioned.gravity = 4; /* to the right */
+ positioned.anchor = (struct yasl_private_rect2i){1900, 100, 20, 20};
+ positioned.constraints = 4; /* flip X */
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x == 1800 && actual_size.x == 100);
+ positioned.gravity = 0;
+ positioned.constraints = 1; /* slide X */
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x + actual_size.x == 1920);
+ positioned.content_offset = (struct yasl_private_vector2i){0, 0};
+ positioned.gravity = 4;
+ positioned.constraints = 16; /* resize X */
+ positioned.anchor = (struct yasl_private_rect2i){1880, 100, 20, 20};
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x == 1920 - actual_size.x && actual_size.x == 20);
+
valid_spec.beside = true;
- popup_calculate_position(d, s, &valid_spec, &x, &y);
+ popup_calculate_position(d, s, &valid_spec, &x, &y, &actual_size);
assert(x == 30 + 50 - 5);
assert(y == 40 - 10);
@@ -94,7 +130,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(popup->parent == s);
assert(popup->size.x == 100 && popup->size.y == 120);
- struct yasl_popup_spec repo_spec = {
+ struct yasl_private_popup_spec repo_spec = {
.size = {150, 180},
.anchor = {10, 20, 30, 40},
.beside = false,
@@ -113,24 +149,24 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
struct drag_test_recorder rec1 = {0};
struct drag_test_recorder rec2 = {0};
- struct yasl_window_cbs cbs1 = {.drag = on_test_drag};
- struct yasl_window_cbs cbs2 = {.drag = on_test_drag};
+ struct yasl_private_window_cbs cbs1 = {.drag = on_test_drag};
+ struct yasl_private_window_cbs cbs2 = {.drag = on_test_drag};
set_callbacks(&s->base, &cbs1, &rec1);
set_callbacks(&s2.base, &cbs2, &rec2);
- enum yasl_drag_end end_reason = (enum yasl_drag_end)-1;
- struct yasl_drag_listener l = {.end = on_test_drag_end};
+ enum yasl_private_drag_end end_reason = (enum yasl_private_drag_end)-1;
+ struct yasl_private_drag_listener l = {.end = on_test_drag_end};
assert(drag_start(&d->base, &s->base, NULL, &l, &end_reason));
assert(d->drag.active);
- /* Motion on s (window 42) -> YASL_DRAG_OVER on s */
+ /* Motion on s (window 42) -> YASL_PRIVATE_DRAG_OVER on s */
XIDeviceEvent mev = {.evtype = XI_Motion, .sourceid = 8, .event = 42, .event_x = 12.0, .event_y = 34.0};
XGenericEventCookie cookie = {.evtype = XI_Motion, .data = &mev};
xi_event(d, &cookie);
assert(rec1.over_count == 1);
assert(rec1.last_pos.x == 12 && rec1.last_pos.y == 34);
- /* Motion on s2 (window 43) -> YASL_DRAG_LEAVE on s, YASL_DRAG_OVER on s2 */
+ /* Motion on s2 (window 43) -> YASL_PRIVATE_DRAG_LEAVE on s, YASL_PRIVATE_DRAG_OVER on s2 */
mev.event = 43;
mev.event_x = 56.0;
mev.event_y = 78.0;
@@ -139,12 +175,12 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(rec2.over_count == 1);
assert(rec2.last_pos.x == 56 && rec2.last_pos.y == 78);
- /* ButtonRelease -> YASL_DRAG_DROP on s2 and drag end */
+ /* ButtonRelease -> YASL_PRIVATE_DRAG_DROP on s2 and drag end */
XIDeviceEvent rel = {.evtype = XI_ButtonRelease, .sourceid = 8, .event = 43, .detail = 1};
cookie = (XGenericEventCookie){.evtype = XI_ButtonRelease, .data = &rel};
xi_event(d, &cookie);
assert(rec2.drop_count == 1);
- assert(end_reason == YASL_DRAG_DROPPED);
+ assert(end_reason == YASL_PRIVATE_DRAG_DROPPED);
assert(!d->drag.active);
/* Drag cancel */
@@ -155,7 +191,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(rec1.over_count == 2);
drag_cancel(&d->base);
assert(rec1.leave_count == 2);
- assert(end_reason == YASL_DRAG_CANCELLED);
+ assert(end_reason == YASL_PRIVATE_DRAG_CANCELLED);
assert(!d->drag.active);
/* Hierarchy change cancels drag and pushes reset */
@@ -164,18 +200,18 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
XIHierarchyEvent hiev = {.evtype = XI_HierarchyChanged, .flags = XISlaveRemoved};
cookie = (XGenericEventCookie){.evtype = XI_HierarchyChanged, .data = &hiev};
xi_event(d, &cookie);
- assert(end_reason == YASL_DRAG_CANCELLED);
+ assert(end_reason == YASL_PRIVATE_DRAG_CANCELLED);
assert(!d->drag.active);
- struct yasl_event reset_ev;
- assert(yasl_surface_poll_event(&s->base, &reset_ev));
- assert(reset_ev.type == YASL_EVENT_INPUT_RESET);
+ struct yasl_private_event reset_ev;
+ assert(yasl_private_surface_poll_event(&s->base, &reset_ev));
+ assert(reset_ev.type == YASL_PRIVATE_EVENT_INPUT_RESET);
/* 4. Drag image parameter */
uint32_t pixels[16] = {0xFF00FF00};
- struct yasl_drag_image img = {.pixels = pixels, .size = {4, 4}, .hotspot = {2, 2}};
+ struct yasl_private_drag_image img = {.pixels = pixels, .size = {4, 4}, .hotspot = {2, 2}};
assert(drag_start(&d->base, &s->base, &img, &l, &end_reason));
assert(d->drag.active);
- assert(drag_carry(&s->base, (struct yasl_vector2i){10, 10}));
+ assert(drag_carry(&s->base, (struct yasl_private_vector2i){10, 10}));
drag_cancel(&d->base);
/* 5. XDND drop-target events and URI parsing */
@@ -246,11 +282,11 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(drec.leave_count == 2);
assert(d->pending_drop.active);
- /* Push YASL_EVENT_DROP */
- push(s, (struct yasl_event){.type = YASL_EVENT_DROP}, "file:///sample.png\r\n", 20);
- struct yasl_event drop_ev;
- assert(yasl_surface_poll_event(&s->base, &drop_ev));
- assert(drop_ev.type == YASL_EVENT_DROP);
+ /* Push YASL_PRIVATE_EVENT_DROP */
+ push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_DROP}, "file:///sample.png\r\n", 20);
+ struct yasl_private_event drop_ev;
+ assert(yasl_private_surface_poll_event(&s->base, &drop_ev));
+ assert(drop_ev.type == YASL_PRIVATE_EVENT_DROP);
assert(drop_ev.text.len == 20);
assert(!strcmp(drop_ev.text.data, "file:///sample.png\r\n"));
@@ -262,10 +298,18 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
int main(void)
{
+ /* External GPU surfaces retain geometry without allocating CPU targets. */
+ struct x11_surface external = {0};
+ external.base.presenter = YASL_PRIVATE_PRESENTER_NONE;
+ assert(resize(&external, (struct yasl_private_vector2i){640, 480}));
+ assert(external.size.x == 640 && external.size.y == 480 && !external.image && !external.pixels);
+
struct x11_display display = {
- .base.ops = &yasl_x11_backend, .abs_pressure = 10, .abs_tilt_x = 11, .abs_tilt_y = 12,
+ .base.ops = &yasl_private_x11_backend, .abs_pressure = 10, .abs_tilt_x = 11, .abs_tilt_y = 12,
};
- struct x11_surface surface = {.base.display = &display.base, .window = 42};
+ struct x11_surface surface = {.base = {.display = &display.base,
+ .creation_flags = YASL_PRIVATE_SURFACE_CPU,
+ .presenter = YASL_PRIVATE_PRESENTER_CPU}, .window = 42};
display.surfaces = &surface;
XIValuatorClassInfo pressure = {.type = XIValuatorClass, .number = 2,
.label = 10, .min = 100, .max = 1100, .mode = XIModeAbsolute};
@@ -277,7 +321,7 @@ int main(void)
XIDeviceInfo info = {.deviceid = 8, .name = "Tablet pen", .num_classes = 3, .classes = classes};
struct x11_pointer pointer = {0};
pointer_info(&display, &pointer, &info);
- assert(pointer.device == YASL_PTR_DEV_PEN);
+ assert(pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
display.pointers = &pointer; /* Cache avoids requiring a real X server. */
unsigned char mask[1] = {0};
@@ -290,9 +334,15 @@ int main(void)
.mods.effective = ShiftMask, .valuators = {sizeof(mask), mask, values}};
XGenericEventCookie cookie = {.evtype = XI_ButtonPress, .data = &sample};
xi_event(&display, &cookie);
- struct yasl_event ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_DOWN && ev.pointer.button == YASL_BUTTON_PRIMARY && ev.pointer.device == YASL_PTR_DEV_PEN);
- assert(ev.pointer.pos.x == 201 && ev.pointer.pos.y == 175 && ev.pointer.mods == YASL_MOD_SHIFT);
+ struct yasl_private_event ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.device_id == 8 && !ev.pen.tool_id);
+ assert(ev.pen.tool == 1 && ev.pen.action == YASL_PRIVATE_PEN_DOWN && ev.pen.in_contact);
+ assert((ev.pen.axes_valid & (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT)) ==
+ (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT));
+ assert(ev.pen.axes_changed == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT));
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_DOWN && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && ev.pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
+ assert(ev.pointer.pos.x == 201 && ev.pointer.pos.y == 175 && ev.pointer.mods == YASL_PRIVATE_MOD_SHIFT);
assert(ev.pointer.pen.pressure == 32768 && ev.pointer.pen.tilt_x == -35 && ev.pointer.pen.tilt_y == 42);
/* XI2 sends only changed axes, with values packed in mask order. */
@@ -302,36 +352,48 @@ int main(void)
sample.evtype = cookie.evtype = XI_Motion;
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.action == YASL_PRIVATE_PEN_MOVE &&
+ ev.pen.axes_valid == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT) &&
+ ev.pen.axes_changed == YASL_PRIVATE_PEN_PRESSURE && ev.pen.pressure == 1.0);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
assert(ev.pointer.pen.tilt_x == -35 && ev.pointer.pen.tilt_y == 42);
values[0] = 5000;
xi_event(&display, &cookie);
+ assert(next(&surface).type == YASL_PRIVATE_EVENT_PEN);
assert(next(&surface).pointer.pen.pressure == 65535);
values[0] = -50;
xi_event(&display, &cookie);
+ assert(next(&surface).type == YASL_PRIVATE_EVENT_PEN);
assert(next(&surface).pointer.pen.pressure == 0);
info.name = "Tablet ERASER";
pointer_info(&display, &pointer, &info);
- assert(pointer.device == YASL_PTR_DEV_ERASER);
+ assert(pointer.device == YASL_PRIVATE_PTR_DEV_ERASER);
values[0] = 900;
sample.evtype = cookie.evtype = XI_ButtonPress;
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.pointer.device == YASL_PTR_DEV_ERASER && ev.pointer.pen.pressure > 0);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.tool == 2 && ev.pen.action == YASL_PRIVATE_PEN_DOWN);
+ ev = next(&surface);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && ev.pointer.pen.pressure > 0);
/* Release must clear pressure even when the driver omits that valuator. */
sample.valuators.mask_len = 0;
sample.evtype = cookie.evtype = XI_ButtonRelease;
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_UP && ev.pointer.button == YASL_BUTTON_PRIMARY && ev.pointer.pen.pressure == 0);
- assert(ev.pointer.device == YASL_PTR_DEV_ERASER);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.action == YASL_PRIVATE_PEN_UP && !ev.pen.in_contact);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_UP && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && ev.pointer.pen.pressure == 0);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER);
XIEnterEvent crossing = {.sourceid = 8, .event = 42, .event_x = 200, .event_y = 175};
cookie = (XGenericEventCookie){.evtype = XI_Leave, .data = &crossing};
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PTR_DEV_ERASER && !ev.pointer.pen.pressure);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.action == YASL_PRIVATE_PEN_LEAVE);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && !ev.pointer.pen.pressure);
/* No tablet axes must produce an ordinary, pressure-free mouse. */
info.num_classes = 0;
@@ -340,24 +402,48 @@ int main(void)
sample.detail = 3;
xi_pointer_event(&surface, &sample);
ev = next(&surface);
- assert(ev.pointer.device == YASL_PTR_DEV_MOUSE && ev.pointer.button == YASL_BUTTON_SECONDARY && ev.pointer.pen.pressure == 0);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE && ev.pointer.button == YASL_PRIVATE_BUTTON_SECONDARY && ev.pointer.pen.pressure == 0);
for (int button = 4; button <= 7; button++) {
sample.detail = button;
sample.evtype = XI_ButtonPress;
xi_pointer_event(&surface, &sample);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_SCROLL);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL);
assert(ev.pointer.scroll.x == (button == 6 ? -1 : button == 7 ? 1 : 0));
assert(ev.pointer.scroll.y == (button == 4 ? -1 : button == 5 ? 1 : 0));
+ assert(ev.pointer.scroll_valid == 1u && ev.pointer.scroll_source == 1u && ev.pointer.scroll_phase == 2u);
sample.evtype = XI_ButtonRelease;
xi_pointer_event(&surface, &sample);
- struct yasl_event unused;
- assert(!yasl_surface_poll_event(&surface.base, &unused));
+ struct yasl_private_event unused;
+ assert(!input_queue_pop(&surface.input, &unused));
}
+ /* XI2 smooth-scroll valuators preserve fractional line data and source. */
+ XIValuatorClassInfo scroll_valuator = {.type = XIValuatorClass, .number = 9,
+ .value = 0, .mode = XIModeRelative};
+ XIScrollClassInfo scroll_class = {.type = XIScrollClass, .number = 9,
+ .scroll_type = XIScrollTypeVertical, .increment = 2.0};
+ XIAnyClassInfo *scroll_classes[] = {(void *)&scroll_valuator, (void *)&scroll_class};
+ info.name = "Test touchpad";
+ info.num_classes = 2;
+ info.classes = scroll_classes;
+ struct x11_pointer touchpad = {0};
+ pointer_info(&display, &touchpad, &info);
+ display.pointers = &touchpad;
+ unsigned char scroll_mask[2] = {0};
+ XISetMask(scroll_mask, 9);
+ double scroll_value[] = {-1.0};
+ sample.evtype = XI_Motion;
+ sample.valuators = (XIValuatorState){sizeof(scroll_mask), scroll_mask, scroll_value};
+ xi_pointer_event(&surface, &sample);
+ assert(next(&surface).type == YASL_PRIVATE_EVENT_POINTER_MOTION);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL && ev.pointer.scroll_valid == 1u &&
+ ev.pointer.scroll_source == 2u && ev.pointer.scroll.y == -0.5);
+ input_queue_clear(&surface.input);
assert(pointer_button(2) == 1 && pointer_button(8) == 3);
- pointer.pressure = (struct tablet_axis){2, 1, 1, 50};
+ pointer.pressure = (struct tablet_axis){2, 1, 1, 50, true};
assert(pressure_value(&pointer.pressure) == 0);
- pointer.tilt_x = (struct tablet_axis){4, -90, 90, -200};
+ pointer.tilt_x = (struct tablet_axis){4, -90, 90, -200, true};
assert(tilt_value(&pointer.tilt_x) == -90);
pointer.tilt_x.value = 200;
assert(tilt_value(&pointer.tilt_x) == 90);