2 * Copyright © 2014 Red Hat, Inc.
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include <linux/input.h>
41 #include <libinput-util.h>
43 #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
53 int width, height; /* of window */
61 /* scroll bar positions */
66 struct touch touches[32];
68 /* l/m/r mouse buttons */
71 struct libinput_device *devices[50];
75 error(const char *fmt, ...)
78 fprintf(stderr, "error: ");
81 vfprintf(stderr, fmt, args);
88 msg(const char *fmt, ...)
101 printf("%s [path/to/device]\n", program_invocation_short_name);
105 draw(GtkWidget *widget, cairo_t *cr, gpointer data)
107 struct window *w = data;
110 cairo_set_source_rgb(cr, 1, 1, 1);
111 cairo_rectangle(cr, 0, 0, w->width, w->height);
114 /* draw pointer sprite */
115 cairo_set_source_rgb(cr, 0, 0, 0);
117 cairo_move_to(cr, w->x, w->y);
118 cairo_rel_line_to(cr, 10, 15);
119 cairo_rel_line_to(cr, -10, 0);
120 cairo_rel_line_to(cr, 0, -15);
124 /* draw scroll bars */
125 cairo_set_source_rgb(cr, .4, .8, 0);
128 cairo_rectangle(cr, w->vx - 10, w->vy - 20, 20, 40);
129 cairo_rectangle(cr, w->hx - 20, w->hy - 10, 40, 20);
134 cairo_set_source_rgb(cr, .8, .2, .2);
136 ARRAY_FOR_EACH(w->touches, t) {
138 cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
144 cairo_set_source_rgb(cr, .2, .4, .8);
147 cairo_arc(cr, w->absx, w->absy, 10, 0, 2 * M_PI);
153 if (w->l || w->m || w->r) {
154 cairo_set_source_rgb(cr, .2, .8, .8);
156 cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
158 cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
160 cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
164 cairo_set_source_rgb(cr, 0, 0, 0);
165 cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
166 cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
167 cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
175 map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
177 struct window *w = data;
179 gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
189 g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
191 gdk_window_set_cursor(gtk_widget_get_window(w->win),
192 gdk_cursor_new(GDK_BLANK_CURSOR));
196 window_init(struct window *w)
198 memset(w, 0, sizeof(*w));
200 w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
201 gtk_widget_set_events(w->win, 0);
202 gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool");
203 gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768);
204 gtk_window_maximize(GTK_WINDOW(w->win));
205 gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE);
206 gtk_widget_realize(w->win);
207 g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
208 g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(gtk_main_quit), NULL);
210 w->area = gtk_drawing_area_new();
211 gtk_widget_set_events(w->area, 0);
212 gtk_container_add(GTK_CONTAINER(w->win), w->area);
213 gtk_widget_show_all(w->win);
217 window_cleanup(struct window *w)
219 struct libinput_device **dev;
220 ARRAY_FOR_EACH(w->devices, dev) {
222 libinput_device_unref(*dev);
227 change_ptraccel(struct window *w, double amount)
229 struct libinput_device **dev;
231 ARRAY_FOR_EACH(w->devices, dev) {
233 enum libinput_config_status status;
238 if (!libinput_device_config_accel_is_available(*dev))
241 speed = libinput_device_config_accel_get_speed(*dev);
242 speed = clip(speed + amount, -1, 1);
244 status = libinput_device_config_accel_set_speed(*dev, speed);
246 if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
247 msg("%s: failed to change accel to %.2f (%s)\n",
248 libinput_device_get_name(*dev),
250 libinput_config_status_to_str(status));
252 printf("%s: speed is %.2f\n",
253 libinput_device_get_name(*dev),
262 handle_event_device_notify(struct libinput_event *ev)
264 struct libinput_device *dev = libinput_event_get_device(ev);
270 if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED)
275 msg("%s %s\n", libinput_device_get_sysname(dev), type);
277 if (libinput_device_config_tap_get_finger_count(dev) > 0) {
278 enum libinput_config_status status;
279 status = libinput_device_config_tap_set_enabled(dev,
280 LIBINPUT_CONFIG_TAP_ENABLED);
281 if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
282 error("%s: Failed to enable tapping\n",
283 libinput_device_get_sysname(dev));
286 li = libinput_event_get_context(ev);
287 w = libinput_get_user_data(li);
289 if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
290 for (i = 0; i < ARRAY_LENGTH(w->devices); i++) {
291 if (w->devices[i] == NULL) {
292 w->devices[i] = libinput_device_ref(dev);
297 for (i = 0; i < ARRAY_LENGTH(w->devices); i++) {
298 if (w->devices[i] == dev) {
299 libinput_device_unref(w->devices[i]);
300 w->devices[i] = NULL;
308 handle_event_motion(struct libinput_event *ev, struct window *w)
310 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
311 double dx = libinput_event_pointer_get_dx(p),
312 dy = libinput_event_pointer_get_dy(p);
316 w->x = clip(w->x, 0.0, w->width);
317 w->y = clip(w->y, 0.0, w->height);
321 handle_event_absmotion(struct libinput_event *ev, struct window *w)
323 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
324 double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
325 y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
332 handle_event_touch(struct libinput_event *ev, struct window *w)
334 struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
335 int slot = libinput_event_touch_get_seat_slot(t);
339 if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches))
342 touch = &w->touches[slot];
344 if (libinput_event_get_type(ev) == LIBINPUT_EVENT_TOUCH_UP) {
349 x = libinput_event_touch_get_x_transformed(t, w->width),
350 y = libinput_event_touch_get_y_transformed(t, w->height);
358 handle_event_axis(struct libinput_event *ev, struct window *w)
360 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
361 enum libinput_pointer_axis axis = libinput_event_pointer_get_axis(p);
362 double v = libinput_event_pointer_get_axis_value(p);
365 case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
367 w->vy = clip(w->vy, 0, w->height);
369 case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
371 w->hx = clip(w->hx, 0, w->width);
379 handle_event_keyboard(struct libinput_event *ev, struct window *w)
381 struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
382 unsigned int key = libinput_event_keyboard_get_key(k);
384 if (libinput_event_keyboard_get_key_state(k) ==
385 LIBINPUT_KEY_STATE_RELEASED)
392 change_ptraccel(w, 0.1);
395 change_ptraccel(w, -0.1);
405 handle_event_button(struct libinput_event *ev, struct window *w)
407 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
408 unsigned int button = libinput_event_pointer_get_button(p);
411 is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
428 handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
430 struct libinput *li = data;
431 struct window *w = libinput_get_user_data(li);
432 struct libinput_event *ev;
434 libinput_dispatch(li);
436 while ((ev = libinput_get_event(li))) {
437 switch (libinput_event_get_type(ev)) {
438 case LIBINPUT_EVENT_NONE:
440 case LIBINPUT_EVENT_DEVICE_ADDED:
441 case LIBINPUT_EVENT_DEVICE_REMOVED:
442 handle_event_device_notify(ev);
444 case LIBINPUT_EVENT_POINTER_MOTION:
445 handle_event_motion(ev, w);
447 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
448 handle_event_absmotion(ev, w);
450 case LIBINPUT_EVENT_TOUCH_DOWN:
451 case LIBINPUT_EVENT_TOUCH_MOTION:
452 case LIBINPUT_EVENT_TOUCH_UP:
453 handle_event_touch(ev, w);
455 case LIBINPUT_EVENT_POINTER_AXIS:
456 handle_event_axis(ev, w);
458 case LIBINPUT_EVENT_TOUCH_CANCEL:
459 case LIBINPUT_EVENT_TOUCH_FRAME:
461 case LIBINPUT_EVENT_POINTER_BUTTON:
462 handle_event_button(ev, w);
464 case LIBINPUT_EVENT_KEYBOARD_KEY:
465 if (handle_event_keyboard(ev, w)) {
466 libinput_event_destroy(ev);
473 libinput_event_destroy(ev);
474 libinput_dispatch(li);
476 gtk_widget_queue_draw(w->area);
482 sockets_init(struct libinput *li)
484 GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li));
486 g_io_channel_set_encoding(c, NULL, NULL);
487 g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
491 parse_opts(int argc, char *argv[])
494 static struct option long_options[] = {
495 { "help", no_argument, 0, 'h' },
498 int option_index = 0;
501 c = getopt_long(argc, argv, "h", long_options,
520 open_restricted(const char *path, int flags, void *user_data)
522 int fd = open(path, flags);
523 return fd < 0 ? -errno : fd;
527 close_restricted(int fd, void *user_data)
532 static const struct libinput_interface interface = {
533 .open_restricted = open_restricted,
534 .close_restricted = close_restricted,
538 main(int argc, char *argv[])
544 gtk_init(&argc, &argv);
546 if (parse_opts(argc, argv) != 0)
551 error("Failed to initialize udev\n");
553 li = libinput_udev_create_context(&interface, &w, udev);
554 if (!li || libinput_udev_assign_seat(li, "seat0") != 0)
555 error("Failed to initialize context from udev\n");