/* Real Xorg driver -> XI2 -> YASL queue -> the portable example -> CPU pixels. */ #define main tablet_example_main #include "../../examples/tablet.c" #undef main #include #include #include #include #include #include #include #include #include #include #include "inputtest-protocol.h" static void send_all(int fd, const void *message, size_t n) { const char *p = message; while (n) { ssize_t sent = write(fd, p, n); if (sent < 0 && errno == EINTR) continue; assert(sent > 0); p += sent; n -= (size_t)sent; } } static void receive_all(int fd, void *message, size_t n) { char *p = message; while (n) { ssize_t got = read(fd, p, n); if (got < 0 && errno == EINTR) continue; assert(got > 0); p += got; n -= (size_t)got; } } static int connect_tool(const char *path) { int fd = socket(AF_UNIX, SOCK_STREAM, 0); assert(fd >= 0); struct sockaddr_un address = {.sun_family = AF_UNIX}; assert(strlen(path) < sizeof(address.sun_path)); strcpy(address.sun_path, path); assert(connect(fd, (struct sockaddr *)&address, sizeof(address)) == 0); xf86ITEventClientVersion version = {{sizeof(version), XF86IT_EVENT_CLIENT_VERSION}, 1, 1}; send_all(fd, &version, sizeof(version)); xf86ITResponseServerVersion response; receive_all(fd, &response, sizeof(response)); assert(response.header.type == XF86IT_RESPONSE_SERVER_VERSION); assert(response.header.length == sizeof(response) && response.major == 1 && response.minor >= 1); return fd; } static void native_draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud) { struct yasl_frame_context context = { .surface = surface, .size = frame->target.size, .scale_120 = yasl_surface_geometry(surface).scale_120, .cpu = {.pixels = frame->target.pixels, .stride_bytes = frame->target.size.x * (int)sizeof(uint32_t)}, .internal_handle = frame, }; draw(ud, surface, &context); } static void native_configure(struct yasl_surface *surface, struct yasl_geometry geometry, void *ud) { input(ud, &(struct yasl_event){.surface = surface, .type = YASL_EVENT_WINDOW_RESIZE, .resize = {.geometry = geometry}}); } static void native_input(struct app *app, const struct yasl_event *event) { input(app, event); } static void pump(int fd, struct yasl_display *display, struct yasl_surface *surface, struct app *app) { xf86ITEventWaitForSync sync = {{sizeof(sync), XF86IT_EVENT_WAIT_FOR_SYNC}}; send_all(fd, &sync, sizeof(sync)); xf86ITResponseHeader response; receive_all(fd, &response, sizeof(response)); assert(response.type == XF86IT_RESPONSE_SYNC_FINISHED && response.length == sizeof(response)); /* The driver acknowledgment guarantees input processing has finished. * Round-trip on YASL's X connection before draining its event queue. */ XSync(yasl_display_get_native_handle(display), False); assert(yasl_display_dispatch(display, 0) == 0); struct yasl_event event; while (yasl_surface_poll_event(surface, &event)) { native_input(app, &event); yasl_surface_schedule_redraw(surface); } yasl_display_flush(display); assert(!app->failed); } static void set_eraser_property(Display *display) { int count = 0; XIDeviceInfo *devices = XIQueryDevice(display, XIAllDevices, &count); assert(devices); bool found = false; for (int i = 0; i < count; i++) { /* The name deliberately doesn't contain "eraser": exercise the * driver's tool property, independently of the name fallback. */ if (strcmp(devices[i].name, "YASL Test Reverse Tool")) continue; Atom property = XInternAtom(display, "Wacom Tool Type", False); uint32_t eraser = (uint32_t)XInternAtom(display, "ERASER", False); XIChangeProperty(display, devices[i].deviceid, property, XA_ATOM, 32, PropModeReplace, (unsigned char *)&eraser, 1); found = true; } XIFreeDeviceInfo(devices); assert(found); XSync(display, False); } int main(int argc, char **argv) { assert(argc == 3); /* pen socket, eraser socket */ struct app app = {.tablet_supported = true, .backend = YASL_BACKEND_X11}; struct yasl_display_spec ds = {.backend = YASL_BACKEND_X11}; struct yasl_display *display = yasl_display_open(NULL, &ds); assert(display && (yasl_display_caps(display) & YASL_CAP_TABLET)); struct yasl_window_spec ss = {.title = "YASL native pressure test", .size = {500, 400}}; struct yasl_surface *surface = yasl_surface_create_toplevel(display, &ss); assert(surface); struct yasl_window_cbs cbs = {.draw = native_draw, .configure = native_configure}; yasl_surface_set_callbacks(surface, &cbs, &app); yasl_surface_schedule_redraw(surface); Display *x = yasl_display_get_native_handle(display); Window window = (Window)(uintptr_t)yasl_surface_get_native_handle(surface); XMoveWindow(x, window, 0, 0); XSetInputFocus(x, window, RevertToParent, CurrentTime); XSync(x, False); assert(yasl_display_dispatch(display, 0) == 0); yasl_display_flush(display); set_eraser_property(x); int pen_fd = connect_tool(argv[1]), eraser_fd = connect_tool(argv[2]); xf86ITEventMotion motion = {.header = {sizeof(motion), XF86IT_EVENT_MOTION}, .is_absolute = 1}; motion.valuators.mask[0] = (1 << 0) | (1 << 1) | (1 << 4); motion.valuators.valuators[0] = 150.0 * 65535 / (DisplayWidth(x, DefaultScreen(x)) - 1); motion.valuators.valuators[1] = 200.0 * 65535 / (DisplayHeight(x, DefaultScreen(x)) - 1); xf86ITEventProximity proximity = {.header = {sizeof(proximity), XF86IT_EVENT_PROXIMITY}, .is_prox_in = 1, .valuators = motion.valuators}; send_all(pen_fd, &proximity, sizeof(proximity)); send_all(pen_fd, &motion, sizeof(motion)); pump(pen_fd, display, surface, &app); assert(app.visible && !app.down && app.pointer.device == YASL_PTR_DEV_PEN); assert(app.pointer.pos.x == 150 && app.pointer.pos.y == 200); assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == PAPER); xf86ITEventButton button = {.header = {sizeof(button), XF86IT_EVENT_BUTTON}, .is_absolute = 1, .button = 1, .is_press = 1, .valuators = motion.valuators}; button.valuators.valuators[4] = 250; /* Driver pressure range is 0..1000. */ send_all(pen_fd, &button, sizeof(button)); pump(pen_fd, display, surface, &app); assert(app.down && app.pointer.pen.pressure == 16384); assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == INK && app.canvas[(size_t)200 * app.geometry.size.x + 170] == PAPER); motion.valuators.mask[0] = 1 << 4; /* No position change. */ motion.valuators.valuators[4] = 1000; send_all(pen_fd, &motion, sizeof(motion)); pump(pen_fd, display, surface, &app); assert(app.down && app.pointer.pen.pressure == 65535); assert(app.canvas[(size_t)200 * app.geometry.size.x + 170] == INK); /* Check the actual window's pixels too, after the example presents. */ XImage *image = XGetImage(x, window, 170, 200, 1, 1, AllPlanes, ZPixmap); assert(image && (XGetPixel(image, 0, 0) & 0xffffff) == (INK & 0xffffff)); XDestroyImage(image); button.is_press = 0; button.valuators.mask[0] = 0; /* Release omits all valuators. */ send_all(pen_fd, &button, sizeof(button)); pump(pen_fd, display, surface, &app); assert(!app.down && !app.pointer.pen.pressure); send_all(eraser_fd, &proximity, sizeof(proximity)); button.is_press = 1; button.valuators = proximity.valuators; button.valuators.valuators[4] = 1000; send_all(eraser_fd, &button, sizeof(button)); pump(eraser_fd, display, surface, &app); assert(app.down && app.pointer.device == YASL_PTR_DEV_ERASER && app.pointer.pen.pressure == 65535); assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == PAPER && app.canvas[(size_t)200 * app.geometry.size.x + 170] == PAPER); button.is_press = 0; send_all(eraser_fd, &button, sizeof(button)); pump(eraser_fd, display, surface, &app); assert(!app.down && !app.pointer.pen.pressure); close(pen_fd); close(eraser_fd); free(app.canvas); yasl_surface_destroy(surface); yasl_display_close(display); puts("Native Xorg pen pressure, stationary pressure, release, and eraser drawing passed."); return 0; }