/* * Copyright © 2014 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "shared.h" #define clip(val_, min_, max_) min((max_), max((min_), (val_))) struct tools_context context; struct touch { int active; int x, y; }; struct point { double x, y; }; struct window { GtkWidget *win; GtkWidget *area; int width, height; /* of window */ /* sprite position */ double x, y; /* abs position */ int absx, absy; /* scroll bar positions */ double vx, vy; double hx, hy; /* touch positions */ struct touch touches[32]; /* l/m/r mouse buttons */ int l, m, r; /* touchpad swipe */ struct { int nfingers; double x, y; } swipe; struct { int nfingers; double scale; double angle; double x, y; } pinch; struct { double x, y; double x_in, y_in; double x_down, y_down; double x_up, y_up; double pressure; double distance; double tilt_x, tilt_y; /* these are for the delta coordinates, but they're not * deltas, they are converted into abs positions */ size_t ndeltas; struct point deltas[64]; } tool; struct libinput_device *devices[50]; }; LIBINPUT_ATTRIBUTE_PRINTF(1, 2) static int error(const char *fmt, ...) { va_list args; fprintf(stderr, "error: "); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); return EXIT_FAILURE; } LIBINPUT_ATTRIBUTE_PRINTF(1, 2) static void msg(const char *fmt, ...) { va_list args; printf("info: "); va_start(args, fmt); vprintf(fmt, args); va_end(args); } static inline void draw_gestures(struct window *w, cairo_t *cr) { int i; int offset; /* swipe */ cairo_save(cr); cairo_translate(cr, w->swipe.x, w->swipe.y); for (i = 0; i < w->swipe.nfingers; i++) { cairo_set_source_rgb(cr, .8, .8, .4); cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI); cairo_fill(cr); } for (i = 0; i < 4; i++) { /* 4 fg max */ cairo_set_source_rgb(cr, 0, 0, 0); cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI); cairo_stroke(cr); } cairo_restore(cr); /* pinch */ cairo_save(cr); offset = w->pinch.scale * 100; cairo_translate(cr, w->pinch.x, w->pinch.y); cairo_rotate(cr, w->pinch.angle * M_PI/180.0); if (w->pinch.nfingers > 0) { cairo_set_source_rgb(cr, .4, .4, .8); cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI); cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI); cairo_fill(cr); } cairo_set_source_rgb(cr, 0, 0, 0); cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI); cairo_stroke(cr); cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI); cairo_stroke(cr); cairo_restore(cr); } static inline void draw_scrollbars(struct window *w, cairo_t *cr) { cairo_set_source_rgb(cr, .4, .8, 0); cairo_save(cr); cairo_rectangle(cr, w->vx - 10, w->vy - 20, 20, 40); cairo_rectangle(cr, w->hx - 20, w->hy - 10, 40, 20); cairo_fill(cr); cairo_restore(cr); } static inline void draw_touchpoints(struct window *w, cairo_t *cr) { struct touch *t; cairo_set_source_rgb(cr, .8, .2, .2); ARRAY_FOR_EACH(w->touches, t) { cairo_save(cr); cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI); cairo_fill(cr); cairo_restore(cr); } } static inline void draw_abs_pointer(struct window *w, cairo_t *cr) { cairo_set_source_rgb(cr, .2, .4, .8); cairo_save(cr); cairo_arc(cr, w->absx, w->absy, 10, 0, 2 * M_PI); cairo_fill(cr); cairo_restore(cr); } static inline void draw_buttons(struct window *w, cairo_t *cr) { cairo_save(cr); if (w->l || w->m || w->r) { cairo_set_source_rgb(cr, .2, .8, .8); if (w->l) cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30); if (w->m) cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30); if (w->r) cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30); cairo_fill(cr); } cairo_set_source_rgb(cr, 0, 0, 0); cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30); cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30); cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30); cairo_stroke(cr); cairo_restore(cr); } static inline void draw_tablet(struct window *w, cairo_t *cr) { double x, y; int first, last; size_t mask; int i; /* tablet tool, square for prox-in location */ cairo_save(cr); cairo_set_source_rgb(cr, .8, .8, .8); if (w->tool.x_in && w->tool.y_in) { cairo_rectangle(cr, w->tool.x_in - 15, w->tool.y_in - 15, 30, 30); cairo_stroke(cr); cairo_restore(cr); cairo_save(cr); } if (w->tool.x_down && w->tool.y_down) { cairo_rectangle(cr, w->tool.x_down - 10, w->tool.y_down - 10, 20, 20); cairo_stroke(cr); cairo_restore(cr); cairo_save(cr); } if (w->tool.x_up && w->tool.y_up) { cairo_rectangle(cr, w->tool.x_up - 10, w->tool.y_up - 10, 20, 20); cairo_stroke(cr); cairo_restore(cr); cairo_save(cr); } if (w->tool.pressure) cairo_set_source_rgb(cr, .8, .8, .2); cairo_translate(cr, w->tool.x, w->tool.y); cairo_scale(cr, 1.0 + w->tool.tilt_x/30.0, 1.0 + w->tool.tilt_y/30.0); cairo_arc(cr, 0, 0, 1 + 10 * max(w->tool.pressure, w->tool.distance), 0, 2 * M_PI); cairo_fill(cr); cairo_restore(cr); /* tablet deltas */ mask = ARRAY_LENGTH(w->tool.deltas); first = max(w->tool.ndeltas + 1, mask) - mask; last = w->tool.ndeltas; cairo_save(cr); cairo_set_source_rgb(cr, .8, .8, .2); x = w->tool.deltas[first % mask].x; y = w->tool.deltas[first % mask].y; cairo_move_to(cr, x, y); for (i = first + 1; i < last; i++) { x = w->tool.deltas[i % mask].x; y = w->tool.deltas[i % mask].y; cairo_line_to(cr, x, y); } cairo_stroke(cr); } static inline void draw_pointer(struct window *w, cairo_t *cr) { /* draw pointer sprite */ cairo_set_source_rgb(cr, 0, 0, 0); cairo_save(cr); cairo_move_to(cr, w->x, w->y); cairo_rel_line_to(cr, 10, 15); cairo_rel_line_to(cr, -10, 0); cairo_rel_line_to(cr, 0, -15); cairo_fill(cr); cairo_restore(cr); } static inline void draw_background(struct window *w, cairo_t *cr) { int x1, x2, y1, y2, x3, y3, x4, y4; int cols; /* 10px and 5px grids */ cairo_save(cr); cairo_set_source_rgb(cr, 0.8, 0.8, 0.8); x1 = w->width/2 - 200; y1 = w->height/2 - 200; x2 = w->width/2 + 200; y2 = w->height/2 - 200; for (cols = 1; cols < 10; cols++) { cairo_move_to(cr, x1 + 10 * cols, y1); cairo_rel_line_to(cr, 0, 100); cairo_move_to(cr, x1, y1 + 10 * cols); cairo_rel_line_to(cr, 100, 0); cairo_move_to(cr, x2 + 5 * cols, y2); cairo_rel_line_to(cr, 0, 50); cairo_move_to(cr, x2, y2 + 5 * cols); cairo_rel_line_to(cr, 50, 0); } /* 3px horiz/vert bar codes */ x3 = w->width/2 - 200; y3 = w->height/2 + 200; x4 = w->width/2 + 200; y4 = w->height/2 + 100; for (cols = 0; cols < 50; cols++) { cairo_move_to(cr, x3 + 3 * cols, y3); cairo_rel_line_to(cr, 0, 20); cairo_move_to(cr, x4, y4 + 3 * cols); cairo_rel_line_to(cr, 20, 0); } cairo_stroke(cr); } static gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data) { struct window *w = data; cairo_set_source_rgb(cr, 1, 1, 1); cairo_rectangle(cr, 0, 0, w->width, w->height); cairo_fill(cr); draw_background(w, cr); draw_gestures(w, cr); draw_scrollbars(w, cr); draw_touchpoints(w, cr); draw_abs_pointer(w, cr); draw_buttons(w, cr); draw_tablet(w, cr); draw_pointer(w, cr); return TRUE; } static void map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data) { struct window *w = data; GdkDisplay *display; GdkWindow *window; gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height); w->x = w->width/2; w->y = w->height/2; w->vx = w->width/2; w->vy = w->height/2; w->hx = w->width/2; w->hy = w->height/2; w->swipe.x = w->width/2; w->swipe.y = w->height/2; w->pinch.scale = 1.0; w->pinch.x = w->width/2; w->pinch.y = w->height/2; g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w); window = gdk_event_get_window(event); display = gdk_window_get_display(window); gdk_window_set_cursor(gtk_widget_get_window(w->win), gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR)); } static void window_init(struct window *w) { memset(w, 0, sizeof(*w)); w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_events(w->win, 0); gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool"); gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768); gtk_window_maximize(GTK_WINDOW(w->win)); gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE); gtk_widget_realize(w->win); g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w); g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(gtk_main_quit), NULL); w->area = gtk_drawing_area_new(); gtk_widget_set_events(w->area, 0); gtk_container_add(GTK_CONTAINER(w->win), w->area); gtk_widget_show_all(w->win); } static void window_cleanup(struct window *w) { struct libinput_device **dev; ARRAY_FOR_EACH(w->devices, dev) { if (*dev) libinput_device_unref(*dev); } } static void change_ptraccel(struct window *w, double amount) { struct libinput_device **dev; ARRAY_FOR_EACH(w->devices, dev) { double speed; enum libinput_config_status status; if (*dev == NULL) continue; if (!libinput_device_config_accel_is_available(*dev)) continue; speed = libinput_device_config_accel_get_speed(*dev); speed = clip(speed + amount, -1, 1); status = libinput_device_config_accel_set_speed(*dev, speed); if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) { msg("%s: failed to change accel to %.2f (%s)\n", libinput_device_get_name(*dev), speed, libinput_config_status_to_str(status)); } else { printf("%s: speed is %.2f\n", libinput_device_get_name(*dev), speed); } } } static void handle_event_device_notify(struct libinput_event *ev) { struct tools_context *context; struct libinput_device *dev = libinput_event_get_device(ev); struct libinput *li; struct window *w; const char *type; size_t i; if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) type = "added"; else type = "removed"; msg("%s %-30s %s\n", libinput_device_get_sysname(dev), libinput_device_get_name(dev), type); li = libinput_event_get_context(ev); context = libinput_get_user_data(li); w = context->user_data; tools_device_apply_config(libinput_event_get_device(ev), &context->options); if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) { for (i = 0; i < ARRAY_LENGTH(w->devices); i++) { if (w->devices[i] == NULL) { w->devices[i] = libinput_device_ref(dev); break; } } } else { for (i = 0; i < ARRAY_LENGTH(w->devices); i++) { if (w->devices[i] == dev) { libinput_device_unref(w->devices[i]); w->devices[i] = NULL; break; } } } } static void handle_event_motion(struct libinput_event *ev, struct window *w) { struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); double dx = libinput_event_pointer_get_dx(p), dy = libinput_event_pointer_get_dy(p); w->x += dx; w->y += dy; w->x = clip(w->x, 0.0, w->width); w->y = clip(w->y, 0.0, w->height); } static void handle_event_absmotion(struct libinput_event *ev, struct window *w) { struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width), y = libinput_event_pointer_get_absolute_y_transformed(p, w->height); w->absx = x; w->absy = y; } static void handle_event_touch(struct libinput_event *ev, struct window *w) { struct libinput_event_touch *t = libinput_event_get_touch_event(ev); int slot = libinput_event_touch_get_seat_slot(t); struct touch *touch; double x, y; if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches)) return; touch = &w->touches[slot]; if (libinput_event_get_type(ev) == LIBINPUT_EVENT_TOUCH_UP) { touch->active = 0; return; } x = libinput_event_touch_get_x_transformed(t, w->width), y = libinput_event_touch_get_y_transformed(t, w->height); touch->active = 1; touch->x = (int)x; touch->y = (int)y; } static void handle_event_axis(struct libinput_event *ev, struct window *w) { struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); double value; if (libinput_event_pointer_has_axis(p, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) { value = libinput_event_pointer_get_axis_value(p, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); w->vy += value; w->vy = clip(w->vy, 0, w->height); } if (libinput_event_pointer_has_axis(p, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) { value = libinput_event_pointer_get_axis_value(p, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); w->hx += value; w->hx = clip(w->hx, 0, w->width); } } static int handle_event_keyboard(struct libinput_event *ev, struct window *w) { struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev); unsigned int key = libinput_event_keyboard_get_key(k); if (libinput_event_keyboard_get_key_state(k) == LIBINPUT_KEY_STATE_RELEASED) return 0; switch(key) { case KEY_ESC: return 1; case KEY_UP: change_ptraccel(w, 0.1); break; case KEY_DOWN: change_ptraccel(w, -0.1); break; default: break; } return 0; } static void handle_event_button(struct libinput_event *ev, struct window *w) { struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); unsigned int button = libinput_event_pointer_get_button(p); int is_press; is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED; switch (button) { case BTN_LEFT: w->l = is_press; break; case BTN_RIGHT: w->r = is_press; break; case BTN_MIDDLE: w->m = is_press; break; } } static void handle_event_swipe(struct libinput_event *ev, struct window *w) { struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev); int nfingers; double dx, dy; nfingers = libinput_event_gesture_get_finger_count(g); switch (libinput_event_get_type(ev)) { case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: w->swipe.nfingers = nfingers; w->swipe.x = w->width/2; w->swipe.y = w->height/2; break; case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: dx = libinput_event_gesture_get_dx(g); dy = libinput_event_gesture_get_dy(g); w->swipe.x += dx; w->swipe.y += dy; break; case LIBINPUT_EVENT_GESTURE_SWIPE_END: w->swipe.nfingers = 0; w->swipe.x = w->width/2; w->swipe.y = w->height/2; break; default: abort(); } } static void handle_event_pinch(struct libinput_event *ev, struct window *w) { struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev); int nfingers; double dx, dy; nfingers = libinput_event_gesture_get_finger_count(g); switch (libinput_event_get_type(ev)) { case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: w->pinch.nfingers = nfingers; w->pinch.x = w->width/2; w->pinch.y = w->height/2; break; case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: dx = libinput_event_gesture_get_dx(g); dy = libinput_event_gesture_get_dy(g); w->pinch.x += dx; w->pinch.y += dy; w->pinch.scale = libinput_event_gesture_get_scale(g); w->pinch.angle += libinput_event_gesture_get_angle_delta(g); break; case LIBINPUT_EVENT_GESTURE_PINCH_END: w->pinch.nfingers = 0; w->pinch.x = w->width/2; w->pinch.y = w->height/2; w->pinch.angle = 0.0; w->pinch.scale = 1.0; break; default: abort(); } } static void handle_event_tablet(struct libinput_event *ev, struct window *w) { struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev); double x, y; struct point point; int idx; const int mask = ARRAY_LENGTH(w->tool.deltas); x = libinput_event_tablet_tool_get_x_transformed(t, w->width); y = libinput_event_tablet_tool_get_y_transformed(t, w->height); switch (libinput_event_get_type(ev)) { case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: if (libinput_event_tablet_tool_get_proximity_state(t) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) { w->tool.x_in = 0; w->tool.y_in = 0; w->tool.x_down = 0; w->tool.y_down = 0; w->tool.x_up = 0; w->tool.y_up = 0; } else { w->tool.x_in = x; w->tool.y_in = y; w->tool.ndeltas = 0; w->tool.deltas[0].x = w->width/2; w->tool.deltas[0].y = w->height/2; } break; case LIBINPUT_EVENT_TABLET_TOOL_TIP: w->tool.pressure = libinput_event_tablet_tool_get_pressure(t); w->tool.distance = libinput_event_tablet_tool_get_distance(t); w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t); w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t); if (libinput_event_tablet_tool_get_tip_state(t) == LIBINPUT_TABLET_TOOL_TIP_DOWN) { w->tool.x_down = x; w->tool.y_down = y; } else { w->tool.x_up = x; w->tool.y_up = y; } /* fallthrough */ case LIBINPUT_EVENT_TABLET_TOOL_AXIS: w->tool.x = x; w->tool.y = y; w->tool.pressure = libinput_event_tablet_tool_get_pressure(t); w->tool.distance = libinput_event_tablet_tool_get_distance(t); w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t); w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t); /* Add the delta to the last position and store them as abs * coordinates */ idx = w->tool.ndeltas % mask; point = w->tool.deltas[idx]; idx = (w->tool.ndeltas + 1) % mask; point.x += libinput_event_tablet_tool_get_dx(t); point.y += libinput_event_tablet_tool_get_dy(t); w->tool.deltas[idx] = point; w->tool.ndeltas++; break; case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: break; default: abort(); } } static gboolean handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) { struct libinput *li = data; struct tools_context *context = libinput_get_user_data(li); struct window *w = context->user_data; struct libinput_event *ev; libinput_dispatch(li); while ((ev = libinput_get_event(li))) { switch (libinput_event_get_type(ev)) { case LIBINPUT_EVENT_NONE: abort(); case LIBINPUT_EVENT_DEVICE_ADDED: case LIBINPUT_EVENT_DEVICE_REMOVED: handle_event_device_notify(ev); break; case LIBINPUT_EVENT_POINTER_MOTION: handle_event_motion(ev, w); break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: handle_event_absmotion(ev, w); break; case LIBINPUT_EVENT_TOUCH_DOWN: case LIBINPUT_EVENT_TOUCH_MOTION: case LIBINPUT_EVENT_TOUCH_UP: handle_event_touch(ev, w); break; case LIBINPUT_EVENT_POINTER_AXIS: handle_event_axis(ev, w); break; case LIBINPUT_EVENT_TOUCH_CANCEL: case LIBINPUT_EVENT_TOUCH_FRAME: break; case LIBINPUT_EVENT_POINTER_BUTTON: handle_event_button(ev, w); break; case LIBINPUT_EVENT_KEYBOARD_KEY: if (handle_event_keyboard(ev, w)) { libinput_event_destroy(ev); gtk_main_quit(); return FALSE; } break; case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: case LIBINPUT_EVENT_GESTURE_SWIPE_END: handle_event_swipe(ev, w); break; case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: case LIBINPUT_EVENT_GESTURE_PINCH_END: handle_event_pinch(ev, w); break; case LIBINPUT_EVENT_TABLET_TOOL_AXIS: case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: case LIBINPUT_EVENT_TABLET_TOOL_TIP: case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: handle_event_tablet(ev, w); break; case LIBINPUT_EVENT_TABLET_PAD_BUTTON: case LIBINPUT_EVENT_TABLET_PAD_RING: case LIBINPUT_EVENT_TABLET_PAD_STRIP: break; case LIBINPUT_EVENT_SWITCH_TOGGLE: break; } libinput_event_destroy(ev); libinput_dispatch(li); } gtk_widget_queue_draw(w->area); return TRUE; } static void sockets_init(struct libinput *li) { GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li)); g_io_channel_set_encoding(c, NULL, NULL); g_io_add_watch(c, G_IO_IN, handle_event_libinput, li); } int main(int argc, char *argv[]) { struct window w; struct libinput *li; struct udev *udev; gtk_init(&argc, &argv); tools_init_context(&context); if (tools_parse_args(argc, argv, &context) != 0) return 1; udev = udev_new(); if (!udev) error("Failed to initialize udev\n"); context.user_data = &w; li = tools_open_backend(&context); if (!li) return 1; window_init(&w); sockets_init(li); handle_event_libinput(NULL, 0, li); gtk_main(); window_cleanup(&w); libinput_unref(li); udev_unref(udev); return 0; }