2014-05-27 15:29:55 +10:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2014 Red Hat, Inc.
|
|
|
|
|
*
|
2015-06-11 12:09:18 +10:00
|
|
|
* 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:
|
2014-05-27 15:29:55 +10:00
|
|
|
*
|
2015-06-11 12:09:18 +10:00
|
|
|
* 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.
|
2014-05-27 15:29:55 +10:00
|
|
|
*/
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include <linux/input.h>
|
|
|
|
|
|
2019-09-06 11:42:09 +10:00
|
|
|
#include <assert.h>
|
2014-05-27 15:29:55 +10:00
|
|
|
#include <cairo.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
2017-06-19 18:38:33 +10:00
|
|
|
#include <getopt.h>
|
2014-05-27 15:29:55 +10:00
|
|
|
#include <math.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2017-12-01 09:31:07 +10:00
|
|
|
#include <stdarg.h>
|
2014-05-27 15:29:55 +10:00
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
#include <glib.h>
|
2018-11-01 13:56:16 +10:00
|
|
|
#include <glib-unix.h>
|
2018-11-07 11:05:05 +10:00
|
|
|
#include <libevdev/libevdev.h>
|
2014-05-27 15:29:55 +10:00
|
|
|
|
|
|
|
|
#include <libinput.h>
|
2019-09-06 11:42:09 +10:00
|
|
|
#include "util-strings.h"
|
|
|
|
|
#include "util-macros.h"
|
|
|
|
|
#include "util-list.h"
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2014-12-18 14:47:54 +10:00
|
|
|
#include "shared.h"
|
|
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_WAYLAND
|
2021-06-16 22:53:45 +02:00
|
|
|
#include <wayland-client.h>
|
|
|
|
|
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
|
|
|
|
#if HAVE_GTK4
|
|
|
|
|
#include <gdk/wayland/gdkwayland.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <gdk/gdkwayland.h>
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_X11
|
2021-07-29 19:01:44 +02:00
|
|
|
#include <X11/X.h>
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
|
#if HAVE_GTK4
|
|
|
|
|
#include <gdk/x11/gdkx.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <gdk/gdkx.h>
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
#define clip(val_, min_, max_) min((max_), max((min_), (val_)))
|
|
|
|
|
|
2019-02-07 09:30:46 +10:00
|
|
|
enum touch_state {
|
|
|
|
|
TOUCH_ACTIVE,
|
|
|
|
|
TOUCH_ENDED,
|
|
|
|
|
TOUCH_CANCELLED,
|
|
|
|
|
};
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
struct touch {
|
2019-02-07 09:30:46 +10:00
|
|
|
enum touch_state state;
|
2014-05-27 15:29:55 +10:00
|
|
|
int x, y;
|
|
|
|
|
};
|
|
|
|
|
|
2016-01-12 16:39:15 +10:00
|
|
|
struct point {
|
|
|
|
|
double x, y;
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-07 11:05:05 +10:00
|
|
|
struct evdev_device {
|
|
|
|
|
struct list node;
|
|
|
|
|
struct libevdev *evdev;
|
|
|
|
|
struct libinput_device *libinput_device;
|
|
|
|
|
int fd;
|
|
|
|
|
guint source_id;
|
|
|
|
|
};
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
struct window {
|
2018-08-27 11:14:05 +10:00
|
|
|
bool grab;
|
2017-06-19 18:38:33 +10:00
|
|
|
struct tools_options options;
|
2018-11-07 11:05:05 +10:00
|
|
|
struct list evdev_devices;
|
2017-06-19 18:38:33 +10:00
|
|
|
|
2021-06-10 20:07:00 +02:00
|
|
|
GMainLoop *event_loop;
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
GtkWidget *win;
|
|
|
|
|
GtkWidget *area;
|
|
|
|
|
int width, height; /* of window */
|
|
|
|
|
|
|
|
|
|
/* sprite position */
|
2021-03-10 14:53:08 +10:00
|
|
|
struct point pointer;
|
2021-03-10 15:05:59 +10:00
|
|
|
struct point unaccelerated;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2018-08-07 12:42:40 +10:00
|
|
|
/* these are for the delta coordinates, but they're not
|
|
|
|
|
* deltas, they are converted into abs positions */
|
|
|
|
|
size_t ndeltas;
|
|
|
|
|
struct point deltas[64];
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
/* abs position */
|
2021-03-10 14:54:19 +10:00
|
|
|
struct point abs;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-06-16 22:53:45 +02:00
|
|
|
/* Wayland and X11 pointer locking */
|
|
|
|
|
struct {
|
|
|
|
|
bool locked;
|
|
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_WAYLAND
|
2021-06-16 22:53:45 +02:00
|
|
|
struct zwp_pointer_constraints_v1 *wayland_pointer_constraints;
|
|
|
|
|
struct zwp_locked_pointer_v1 *wayland_locked_pointer;
|
|
|
|
|
#endif
|
|
|
|
|
} lock_pointer;
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
/* scroll bar positions */
|
2018-11-28 12:11:08 +10:00
|
|
|
struct {
|
|
|
|
|
double vx, vy;
|
|
|
|
|
double hx, hy;
|
2018-11-28 12:16:51 +10:00
|
|
|
|
|
|
|
|
double vx_discrete, vy_discrete;
|
|
|
|
|
double hx_discrete, hy_discrete;
|
2018-11-28 12:11:08 +10:00
|
|
|
} scroll;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
|
|
|
|
/* touch positions */
|
|
|
|
|
struct touch touches[32];
|
|
|
|
|
|
|
|
|
|
/* l/m/r mouse buttons */
|
2019-02-12 10:05:25 +10:00
|
|
|
struct {
|
|
|
|
|
bool l, m, r;
|
2019-02-12 14:46:58 +10:00
|
|
|
bool other;
|
|
|
|
|
const char *other_name;
|
2019-02-12 10:05:25 +10:00
|
|
|
} buttons;
|
2014-07-04 08:56:44 +10:00
|
|
|
|
2015-05-22 10:40:16 +10:00
|
|
|
/* touchpad swipe */
|
|
|
|
|
struct {
|
|
|
|
|
int nfingers;
|
|
|
|
|
double x, y;
|
|
|
|
|
} swipe;
|
|
|
|
|
|
2015-05-22 11:14:41 +10:00
|
|
|
struct {
|
|
|
|
|
int nfingers;
|
|
|
|
|
double scale;
|
|
|
|
|
double angle;
|
|
|
|
|
double x, y;
|
|
|
|
|
} pinch;
|
|
|
|
|
|
2021-05-27 19:19:38 +02:00
|
|
|
struct {
|
|
|
|
|
int nfingers;
|
|
|
|
|
bool active;
|
|
|
|
|
} hold;
|
|
|
|
|
|
2015-11-05 14:37:29 +10:00
|
|
|
struct {
|
|
|
|
|
double x, y;
|
|
|
|
|
double x_in, y_in;
|
2015-11-11 13:39:43 +10:00
|
|
|
double x_down, y_down;
|
|
|
|
|
double x_up, y_up;
|
2015-11-05 14:37:29 +10:00
|
|
|
double pressure;
|
|
|
|
|
double distance;
|
|
|
|
|
double tilt_x, tilt_y;
|
2018-09-13 13:26:22 +10:00
|
|
|
double rotation;
|
|
|
|
|
double size_major, size_minor;
|
2019-11-01 08:20:07 +10:00
|
|
|
bool is_down;
|
2016-01-12 16:39:15 +10:00
|
|
|
|
|
|
|
|
/* these are for the delta coordinates, but they're not
|
2017-02-27 15:24:09 +10:00
|
|
|
* deltas, they are converted into abs positions */
|
2016-01-12 16:39:15 +10:00
|
|
|
size_t ndeltas;
|
|
|
|
|
struct point deltas[64];
|
2015-11-05 14:37:29 +10:00
|
|
|
} tool;
|
|
|
|
|
|
2019-06-13 10:35:13 +10:00
|
|
|
struct {
|
|
|
|
|
struct {
|
|
|
|
|
double position;
|
|
|
|
|
int number;
|
|
|
|
|
} ring;
|
|
|
|
|
struct {
|
|
|
|
|
double position;
|
|
|
|
|
int number;
|
|
|
|
|
} strip;
|
2024-01-30 14:43:59 +10:00
|
|
|
struct {
|
|
|
|
|
double position;
|
|
|
|
|
int number;
|
|
|
|
|
} dial;
|
2019-06-13 10:35:13 +10:00
|
|
|
} pad;
|
|
|
|
|
|
2018-11-07 11:05:05 +10:00
|
|
|
struct {
|
|
|
|
|
int rel_x, rel_y; /* REL_X/Y */
|
|
|
|
|
int x, y; /* ABS_X/Y */
|
|
|
|
|
struct {
|
|
|
|
|
int x, y; /* ABS_MT_POSITION_X/Y */
|
|
|
|
|
bool active;
|
|
|
|
|
} slots[16];
|
|
|
|
|
unsigned int slot; /* ABS_MT_SLOT */
|
|
|
|
|
/* So we know when to re-fetch the abs axes */
|
|
|
|
|
uintptr_t device, last_device;
|
|
|
|
|
} evdev;
|
|
|
|
|
|
2014-07-04 08:56:44 +10:00
|
|
|
struct libinput_device *devices[50];
|
2014-05-27 15:29:55 +10:00
|
|
|
};
|
|
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_WAYLAND
|
2021-06-16 22:53:45 +02:00
|
|
|
static void
|
|
|
|
|
wayland_registry_global(void *data,
|
|
|
|
|
struct wl_registry *registry,
|
|
|
|
|
uint32_t name,
|
|
|
|
|
const char *interface,
|
|
|
|
|
uint32_t version)
|
|
|
|
|
{
|
|
|
|
|
struct window *w = data;
|
|
|
|
|
|
|
|
|
|
if (!g_strcmp0(interface, "zwp_pointer_constraints_v1")) {
|
|
|
|
|
w->lock_pointer.wayland_pointer_constraints =
|
|
|
|
|
wl_registry_bind(registry,
|
|
|
|
|
name,
|
|
|
|
|
&zwp_pointer_constraints_v1_interface,
|
|
|
|
|
1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wayland_registry_global_remove(void *data,
|
|
|
|
|
struct wl_registry *wl_registry,
|
|
|
|
|
uint32_t name)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-18 16:24:11 +01:00
|
|
|
static struct wl_registry_listener registry_listener = {
|
2021-06-16 22:53:45 +02:00
|
|
|
wayland_registry_global,
|
|
|
|
|
wayland_registry_global_remove
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
wayland_lock_pointer(struct window *w)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *gdk_display;
|
|
|
|
|
GdkSeat *gdk_seat;
|
|
|
|
|
GdkDevice *gdk_device;
|
|
|
|
|
struct wl_display *display;
|
|
|
|
|
struct wl_registry *registry;
|
|
|
|
|
struct wl_pointer *wayland_pointer;
|
|
|
|
|
struct wl_surface *surface;
|
|
|
|
|
|
|
|
|
|
w->lock_pointer.wayland_pointer_constraints = NULL;
|
|
|
|
|
|
|
|
|
|
gdk_display = gdk_display_get_default();
|
|
|
|
|
display = gdk_wayland_display_get_wl_display(gdk_display);
|
|
|
|
|
|
|
|
|
|
gdk_seat = gdk_display_get_default_seat(gdk_display);
|
|
|
|
|
gdk_device = gdk_seat_get_pointer(gdk_seat);
|
|
|
|
|
wayland_pointer = gdk_wayland_device_get_wl_pointer(gdk_device);
|
|
|
|
|
|
|
|
|
|
registry = wl_display_get_registry(display);
|
|
|
|
|
wl_registry_add_listener(registry, ®istry_listener, w);
|
|
|
|
|
wl_display_roundtrip(display);
|
|
|
|
|
|
|
|
|
|
if (!w->lock_pointer.wayland_pointer_constraints)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
#if HAVE_GTK4
|
|
|
|
|
GtkNative *window = gtk_widget_get_native(w->win);
|
|
|
|
|
GdkSurface *gdk_surface = gtk_native_get_surface(window);
|
|
|
|
|
surface = gdk_wayland_surface_get_wl_surface(gdk_surface);
|
|
|
|
|
#else
|
|
|
|
|
GdkWindow *window = gtk_widget_get_window(w->win);
|
|
|
|
|
surface = gdk_wayland_window_get_wl_surface(window);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
w->lock_pointer.wayland_locked_pointer =
|
|
|
|
|
zwp_pointer_constraints_v1_lock_pointer(w->lock_pointer.wayland_pointer_constraints,
|
|
|
|
|
surface,
|
|
|
|
|
wayland_pointer,
|
|
|
|
|
NULL,
|
|
|
|
|
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wayland_unlock_pointer(struct window *w)
|
|
|
|
|
{
|
|
|
|
|
w->lock_pointer.wayland_pointer_constraints = NULL;
|
|
|
|
|
zwp_locked_pointer_v1_destroy(w->lock_pointer.wayland_locked_pointer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
backend_is_wayland(void)
|
|
|
|
|
{
|
|
|
|
|
return GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
|
|
|
|
|
}
|
2022-06-10 10:31:37 +10:00
|
|
|
#endif /* HAVE_GTK_WAYLAND */
|
2021-06-16 22:53:45 +02:00
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_X11
|
2021-07-29 19:01:44 +02:00
|
|
|
static bool
|
|
|
|
|
x_lock_pointer(struct window *w)
|
|
|
|
|
{
|
|
|
|
|
Display *x_display;
|
|
|
|
|
Window x_win;
|
|
|
|
|
int result;
|
|
|
|
|
|
2025-06-19 14:40:11 +10:00
|
|
|
/* gdk_display_get_default() is deprecated but probably won't be removed
|
|
|
|
|
* before GTK X11 is removed completely so this whole section will
|
|
|
|
|
* be gone anyway. Meanwhile, disable the warning
|
|
|
|
|
*/
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
2021-07-29 19:01:44 +02:00
|
|
|
x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
|
2025-06-19 14:40:11 +10:00
|
|
|
#pragma GCC diagnostic pop
|
2021-07-29 19:01:44 +02:00
|
|
|
|
|
|
|
|
#if HAVE_GTK4
|
|
|
|
|
GtkNative *window = gtk_widget_get_native(w->win);
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(window);
|
2025-06-19 14:40:11 +10:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
2021-07-29 19:01:44 +02:00
|
|
|
x_win = GDK_SURFACE_XID(surface);
|
2025-06-19 14:40:11 +10:00
|
|
|
#pragma GCC diagnostic pop
|
2021-07-29 19:01:44 +02:00
|
|
|
#else
|
|
|
|
|
GdkWindow *window = gtk_widget_get_window(w->win);
|
|
|
|
|
x_win = GDK_WINDOW_XID(window);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
result = XGrabPointer(x_display, x_win,
|
|
|
|
|
False, NoEventMask,
|
|
|
|
|
GrabModeAsync, GrabModeAsync,
|
|
|
|
|
x_win,
|
|
|
|
|
None,
|
|
|
|
|
CurrentTime);
|
|
|
|
|
return (result == GrabSuccess);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
x_unlock_pointer(struct window *w)
|
|
|
|
|
{
|
|
|
|
|
Display *x_display;
|
|
|
|
|
|
2025-06-19 14:40:11 +10:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
2021-07-29 19:01:44 +02:00
|
|
|
x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
|
2025-06-19 14:40:11 +10:00
|
|
|
#pragma GCC diagnostic pop
|
2021-07-29 19:01:44 +02:00
|
|
|
|
|
|
|
|
XUngrabPointer(x_display, CurrentTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
backend_is_x11(void)
|
|
|
|
|
{
|
|
|
|
|
return GDK_IS_X11_DISPLAY(gdk_display_get_default());
|
|
|
|
|
}
|
2022-06-10 10:31:37 +10:00
|
|
|
#endif /* HAVE_GTK_X11 */
|
2021-07-29 19:01:44 +02:00
|
|
|
|
2021-06-16 22:53:45 +02:00
|
|
|
static bool
|
|
|
|
|
window_lock_pointer(struct window *w)
|
|
|
|
|
{
|
2022-10-10 08:14:55 +02:00
|
|
|
if (w->lock_pointer.locked)
|
|
|
|
|
return true;
|
2021-06-16 22:53:45 +02:00
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_WAYLAND
|
2021-06-16 22:53:45 +02:00
|
|
|
if (backend_is_wayland())
|
|
|
|
|
w->lock_pointer.locked = wayland_lock_pointer(w);
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_X11
|
2021-07-29 19:01:44 +02:00
|
|
|
if (backend_is_x11())
|
|
|
|
|
w->lock_pointer.locked = x_lock_pointer(w);
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-06-16 22:53:45 +02:00
|
|
|
return w->lock_pointer.locked;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
window_unlock_pointer(struct window *w)
|
|
|
|
|
{
|
|
|
|
|
if (!w->lock_pointer.locked)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
w->lock_pointer.locked = false;
|
|
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_WAYLAND
|
2021-06-16 22:53:45 +02:00
|
|
|
if (backend_is_wayland())
|
|
|
|
|
wayland_unlock_pointer(w);
|
|
|
|
|
#endif
|
2021-07-29 19:01:44 +02:00
|
|
|
|
2022-06-10 10:31:37 +10:00
|
|
|
#if HAVE_GTK_X11
|
2021-07-29 19:01:44 +02:00
|
|
|
if (backend_is_x11())
|
|
|
|
|
x_unlock_pointer(w);
|
|
|
|
|
#endif
|
2021-06-16 22:53:45 +02:00
|
|
|
}
|
|
|
|
|
|
2016-10-24 11:06:23 +10:00
|
|
|
LIBINPUT_ATTRIBUTE_PRINTF(1, 2)
|
2017-06-19 18:38:33 +10:00
|
|
|
static inline void
|
2014-05-27 15:29:55 +10:00
|
|
|
msg(const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list args;
|
|
|
|
|
printf("info: ");
|
|
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
vprintf(fmt, args);
|
|
|
|
|
va_end(args);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-07 11:05:05 +10:00
|
|
|
static inline void
|
|
|
|
|
draw_evdev_rel(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
|
|
|
|
int center_x, center_y;
|
|
|
|
|
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_set_source_rgb(cr, .2, .2, .8);
|
|
|
|
|
center_x = w->width/2 - 400;
|
|
|
|
|
center_y = w->height/2;
|
|
|
|
|
|
|
|
|
|
cairo_arc(cr, center_x, center_y, 10, 0, 2 * M_PI);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
|
|
|
|
|
if (w->evdev.rel_x) {
|
|
|
|
|
int dir = w->evdev.rel_x > 0 ? 1 : -1;
|
|
|
|
|
for (int i = 0; i < abs(w->evdev.rel_x); i++) {
|
|
|
|
|
cairo_move_to(cr,
|
|
|
|
|
center_x + (i + 1) * 20 * dir,
|
|
|
|
|
center_y - 20);
|
|
|
|
|
cairo_rel_line_to(cr, 0, 40);
|
|
|
|
|
cairo_rel_line_to(cr, 20 * dir, -20);
|
|
|
|
|
cairo_rel_line_to(cr, -20 * dir, -20);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (w->evdev.rel_y) {
|
|
|
|
|
int dir = w->evdev.rel_y > 0 ? 1 : -1;
|
|
|
|
|
for (int i = 0; i < abs(w->evdev.rel_y); i++) {
|
|
|
|
|
cairo_move_to(cr,
|
|
|
|
|
center_x - 20,
|
|
|
|
|
center_y + (i + 1) * 20 * dir);
|
|
|
|
|
cairo_rel_line_to(cr, 40, 0);
|
|
|
|
|
cairo_rel_line_to(cr, -20, 20 * dir);
|
|
|
|
|
cairo_rel_line_to(cr, -20, -20 * dir);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
draw_evdev_abs(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
|
|
|
|
static const struct input_absinfo *ax = NULL, *ay = NULL;
|
|
|
|
|
const int normalized_width = 200;
|
|
|
|
|
int outline_width = normalized_width,
|
|
|
|
|
outline_height = normalized_width * 0.75;
|
|
|
|
|
int center_x, center_y;
|
|
|
|
|
int width, height;
|
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
|
|
|
|
|
center_x = w->width/2 + 400;
|
|
|
|
|
center_y = w->height/2;
|
|
|
|
|
|
|
|
|
|
/* Always the outline even if we didn't get any abs events yet so it
|
|
|
|
|
* doesn't just appear out of nowhere */
|
|
|
|
|
if (w->evdev.device == 0)
|
|
|
|
|
goto draw_outline;
|
|
|
|
|
|
|
|
|
|
/* device has changed, so the abs proportions/dimensions have
|
|
|
|
|
* changed. */
|
|
|
|
|
if (w->evdev.device != w->evdev.last_device) {
|
|
|
|
|
struct evdev_device *d;
|
|
|
|
|
|
|
|
|
|
ax = NULL;
|
|
|
|
|
ay = NULL;
|
|
|
|
|
|
|
|
|
|
list_for_each(d, &w->evdev_devices, node) {
|
|
|
|
|
if ((uintptr_t)d->libinput_device != w->evdev.device)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ax = libevdev_get_abs_info(d->evdev, ABS_X);
|
|
|
|
|
ay = libevdev_get_abs_info(d->evdev, ABS_Y);
|
|
|
|
|
w->evdev.last_device = w->evdev.device;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ax == NULL || ay == NULL)
|
|
|
|
|
goto draw_outline;
|
|
|
|
|
|
|
|
|
|
width = ax->maximum - ax->minimum;
|
|
|
|
|
height = ay->maximum - ay->minimum;
|
|
|
|
|
outline_height = 1.0 * height/width * normalized_width;
|
|
|
|
|
outline_width = normalized_width;
|
|
|
|
|
|
2024-06-13 11:11:57 +10:00
|
|
|
cairo_set_source_rgb(cr, .2, .2, .8);
|
2018-11-07 11:05:05 +10:00
|
|
|
x = 1.0 * (w->evdev.x - ax->minimum)/width * outline_width;
|
|
|
|
|
y = 1.0 * (w->evdev.y - ay->minimum)/height * outline_height;
|
|
|
|
|
x += center_x - outline_width/2;
|
|
|
|
|
y += center_y - outline_height/2;
|
|
|
|
|
cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ARRAY_LENGTH(w->evdev.slots); i++) {
|
|
|
|
|
if (!w->evdev.slots[i].active)
|
|
|
|
|
continue;
|
|
|
|
|
|
2024-12-19 13:51:42 +10:00
|
|
|
cairo_set_source_rgb(cr,
|
|
|
|
|
.2 + .2 * (i % 5),
|
|
|
|
|
.2 + .2 * (i % 5),
|
|
|
|
|
.8 - .2 * (i % 5));
|
2018-11-07 11:05:05 +10:00
|
|
|
x = w->evdev.slots[i].x;
|
|
|
|
|
y = w->evdev.slots[i].y;
|
|
|
|
|
x = 1.0 * (x - ax->minimum)/width * outline_width;
|
|
|
|
|
y = 1.0 * (y - ay->minimum)/height * outline_height;
|
|
|
|
|
x += center_x - outline_width/2;
|
|
|
|
|
y += center_y - outline_height/2;
|
|
|
|
|
cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
|
|
|
|
|
cairo_fill(cr);
|
2023-10-16 04:31:17 +00:00
|
|
|
|
|
|
|
|
char finger_text[3];
|
|
|
|
|
cairo_text_extents_t finger_text_extents;
|
|
|
|
|
snprintf(finger_text, 3, "%zu", i);
|
|
|
|
|
cairo_set_source_rgb(cr, 1.f, 1.f, 1.f);
|
|
|
|
|
cairo_set_font_size(cr, 12.0);
|
|
|
|
|
cairo_text_extents(cr, finger_text, &finger_text_extents);
|
|
|
|
|
cairo_move_to(cr, x - finger_text_extents.width/2,
|
|
|
|
|
y + finger_text_extents.height/2);
|
|
|
|
|
cairo_show_text(cr, finger_text);
|
2018-11-07 11:05:05 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
draw_outline:
|
|
|
|
|
/* The touchpad outline */
|
2023-10-16 04:31:17 +00:00
|
|
|
cairo_set_source_rgb(cr, .2, .2, .8);
|
2018-11-07 11:05:05 +10:00
|
|
|
cairo_rectangle(cr,
|
|
|
|
|
center_x - outline_width/2,
|
|
|
|
|
center_y - outline_height/2,
|
|
|
|
|
outline_width,
|
|
|
|
|
outline_height);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 16:05:25 +10:00
|
|
|
static inline void
|
|
|
|
|
draw_gestures(struct window *w, cairo_t *cr)
|
2014-05-27 15:29:55 +10:00
|
|
|
{
|
2016-01-29 16:05:25 +10:00
|
|
|
int offset;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2015-05-22 10:40:16 +10:00
|
|
|
/* swipe */
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_translate(cr, w->swipe.x, w->swipe.y);
|
2021-05-27 19:19:38 +02:00
|
|
|
for (int i = 0; i < w->swipe.nfingers; i++) {
|
2015-05-22 10:40:16 +10:00
|
|
|
cairo_set_source_rgb(cr, .8, .8, .4);
|
|
|
|
|
cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 19:19:38 +02:00
|
|
|
for (int i = 0; i < 4; i++) { /* 4 fg max */
|
2015-05-22 10:40:16 +10:00
|
|
|
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);
|
|
|
|
|
|
2015-05-22 11:14:41 +10:00
|
|
|
/* 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);
|
2021-05-27 19:19:38 +02:00
|
|
|
|
|
|
|
|
/* hold */
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_translate(cr, w->width/2, w->height/2 + 100);
|
|
|
|
|
|
|
|
|
|
for (int i = 4; i > 0; i--) { /* 4 fg max */
|
|
|
|
|
double r, g, b, hold_alpha;
|
|
|
|
|
|
|
|
|
|
r = .4 + .2 * (i % 2);
|
|
|
|
|
g = .2;
|
|
|
|
|
b = .2;
|
|
|
|
|
hold_alpha = (w->hold.active && i <= w->hold.nfingers) ? 1 : .5;
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgba(cr, r, g, b, hold_alpha);
|
|
|
|
|
cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgba(cr, 0, 0, 0, hold_alpha);
|
|
|
|
|
cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_restore(cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
draw_scrollbars(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2018-11-28 12:16:51 +10:00
|
|
|
/* normal scrollbars */
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_save(cr);
|
2019-02-14 12:44:27 +10:00
|
|
|
cairo_set_source_rgb(cr, .4, .8, 0);
|
2018-11-28 12:11:08 +10:00
|
|
|
cairo_rectangle(cr, w->scroll.vx - 10, w->scroll.vy - 20, 20, 40);
|
|
|
|
|
cairo_rectangle(cr, w->scroll.hx - 20, w->scroll.hy - 10, 40, 20);
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_fill(cr);
|
2018-11-28 12:16:51 +10:00
|
|
|
|
|
|
|
|
/* discrete scrollbars */
|
|
|
|
|
cairo_set_source_rgb(cr, .8, .4, 0);
|
|
|
|
|
cairo_rectangle(cr, w->scroll.vx_discrete - 5, w->scroll.vy_discrete - 10, 10, 20);
|
|
|
|
|
cairo_rectangle(cr, w->scroll.hx_discrete - 10, w->scroll.hy_discrete - 5, 20, 10);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_restore(cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
draw_touchpoints(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
2019-02-14 12:44:27 +10:00
|
|
|
cairo_save(cr);
|
2014-05-27 15:29:55 +10:00
|
|
|
ARRAY_FOR_EACH(w->touches, t) {
|
2019-02-07 09:30:46 +10:00
|
|
|
if (t->state == TOUCH_ACTIVE)
|
|
|
|
|
cairo_set_source_rgb(cr, .8, .2, .2);
|
|
|
|
|
else
|
|
|
|
|
cairo_set_source_rgb(cr, .8, .4, .4);
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
|
2019-02-07 09:30:46 +10:00
|
|
|
if (t->state == TOUCH_CANCELLED)
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
else
|
|
|
|
|
cairo_fill(cr);
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
2019-02-14 12:44:27 +10:00
|
|
|
cairo_restore(cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2016-01-29 16:05:25 +10:00
|
|
|
static inline void
|
|
|
|
|
draw_abs_pointer(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
2014-05-27 15:29:55 +10:00
|
|
|
|
|
|
|
|
cairo_save(cr);
|
2019-02-14 12:44:27 +10:00
|
|
|
cairo_set_source_rgb(cr, .2, .4, .8);
|
2021-03-10 14:54:19 +10:00
|
|
|
cairo_arc(cr, w->abs.x, w->abs.y, 10, 0, 2 * M_PI);
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_fill(cr);
|
|
|
|
|
cairo_restore(cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2019-02-12 14:46:58 +10:00
|
|
|
static inline void
|
2019-06-13 10:35:13 +10:00
|
|
|
draw_text(cairo_t *cr, const char *text, double x, double y)
|
2019-02-12 14:46:58 +10:00
|
|
|
{
|
|
|
|
|
cairo_text_extents_t te;
|
|
|
|
|
cairo_font_extents_t fe;
|
|
|
|
|
|
2019-06-13 10:35:13 +10:00
|
|
|
cairo_text_extents(cr, text, &te);
|
|
|
|
|
cairo_font_extents(cr, &fe);
|
|
|
|
|
/* center of the rectangle */
|
|
|
|
|
cairo_move_to(cr, x, y);
|
|
|
|
|
cairo_rel_move_to(cr, -te.width/2, -fe.descent + te.height/2);
|
|
|
|
|
cairo_show_text(cr, text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
draw_other_button (struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
|
|
|
|
const char *name = w->buttons.other_name;
|
|
|
|
|
|
2019-02-12 14:46:58 +10:00
|
|
|
cairo_save(cr);
|
|
|
|
|
|
|
|
|
|
if (!w->buttons.other)
|
|
|
|
|
goto outline;
|
|
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
|
name = "undefined";
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgb(cr, .2, .8, .8);
|
|
|
|
|
cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgb(cr, 0, 0, 0);
|
2019-06-13 10:35:13 +10:00
|
|
|
|
|
|
|
|
draw_text(cr, name, w->width/2, w->height - 150 + 15);
|
2019-02-12 14:46:58 +10:00
|
|
|
|
|
|
|
|
outline:
|
|
|
|
|
cairo_set_source_rgb(cr, 0, 0, 0);
|
|
|
|
|
cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 16:05:25 +10:00
|
|
|
static inline void
|
|
|
|
|
draw_buttons(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_save(cr);
|
2019-02-12 10:05:25 +10:00
|
|
|
|
|
|
|
|
if (w->buttons.l || w->buttons.m || w->buttons.r) {
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_set_source_rgb(cr, .2, .8, .8);
|
2019-02-12 10:05:25 +10:00
|
|
|
if (w->buttons.l)
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
|
2019-02-12 10:05:25 +10:00
|
|
|
if (w->buttons.m)
|
2014-05-27 15:29:55 +10:00
|
|
|
cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
|
2019-02-12 10:05:25 +10:00
|
|
|
if (w->buttons.r)
|
2014-05-27 15:29:55 +10:00
|
|
|
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);
|
2019-02-12 14:46:58 +10:00
|
|
|
|
|
|
|
|
draw_other_button(w, cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
}
|
|
|
|
|
|
2019-06-13 10:35:13 +10:00
|
|
|
static inline void
|
|
|
|
|
draw_pad(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
|
|
|
|
double rx, ry;
|
|
|
|
|
double pos;
|
|
|
|
|
char number[3];
|
|
|
|
|
|
|
|
|
|
rx = w->width/2 - 200;
|
|
|
|
|
ry = w->height/2 + 100;
|
|
|
|
|
|
|
|
|
|
cairo_save(cr);
|
2024-01-30 14:43:59 +10:00
|
|
|
/* outer ring (for ring) */
|
2019-06-13 10:35:13 +10:00
|
|
|
cairo_set_source_rgb(cr, .7, .7, .0);
|
|
|
|
|
cairo_arc(cr, rx, ry, 50, 0, 2 * M_PI);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
2024-01-30 14:43:59 +10:00
|
|
|
/* inner ring (for dial) */
|
2019-06-13 10:35:13 +10:00
|
|
|
cairo_set_source_rgb(cr, 1., 1., 1.);
|
|
|
|
|
cairo_arc(cr, rx, ry, 30, 0, 2 * M_PI);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
|
|
|
|
/* marker */
|
|
|
|
|
/* libinput has degrees and 0 is north, cairo has radians and 0 is
|
|
|
|
|
* east */
|
|
|
|
|
if (w->pad.ring.position != -1) {
|
|
|
|
|
pos = (w->pad.ring.position + 270) * M_PI/180.0;
|
|
|
|
|
cairo_set_source_rgb(cr, .0, .0, .0);
|
|
|
|
|
cairo_set_line_width(cr, 20);
|
|
|
|
|
cairo_arc(cr, rx, ry, 40, pos - M_PI/8 , pos + M_PI/8);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
|
|
|
|
|
snprintf(number, sizeof(number), "%d", w->pad.ring.number);
|
|
|
|
|
cairo_set_source_rgb(cr, .0, .0, .0);
|
|
|
|
|
draw_text(cr, number, rx, ry);
|
2024-01-30 14:43:59 +10:00
|
|
|
}
|
2019-06-13 10:35:13 +10:00
|
|
|
|
2024-01-30 14:43:59 +10:00
|
|
|
if (w->pad.dial.position != -1) {
|
|
|
|
|
const int degrees_per_click = 15.0;
|
|
|
|
|
double degrees = fmod(w->pad.dial.position/120 * degrees_per_click, 360);
|
|
|
|
|
pos = (degrees + 270) * M_PI/180.0;
|
|
|
|
|
cairo_set_source_rgb(cr, .0, .0, .0);
|
|
|
|
|
cairo_set_line_width(cr, 20);
|
|
|
|
|
cairo_arc(cr, rx, ry, 20, pos - M_PI/12 , pos + M_PI/12);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
|
|
|
|
|
snprintf(number, sizeof(number), "%d", w->pad.dial.number);
|
|
|
|
|
cairo_set_source_rgb(cr, .0, .0, .0);
|
|
|
|
|
draw_text(cr, number, rx, ry);
|
2019-06-13 10:35:13 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
|
|
|
|
|
rx = w->width/2 - 300;
|
|
|
|
|
ry = w->height/2 + 50;
|
|
|
|
|
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_set_source_rgb(cr, .7, .7, .0);
|
|
|
|
|
cairo_rectangle(cr, rx, ry, 20, 100);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
|
|
|
|
if (w->pad.strip.position != -1) {
|
|
|
|
|
pos = w->pad.strip.position * 80;
|
|
|
|
|
cairo_set_source_rgb(cr, .0, .0, .0);
|
|
|
|
|
cairo_rectangle(cr, rx, ry + pos, 20, 20);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
|
|
|
|
snprintf(number, sizeof(number), "%d", w->pad.strip.number);
|
|
|
|
|
cairo_set_source_rgb(cr, .0, .0, .0);
|
|
|
|
|
draw_text(cr, number, rx + 10, ry - 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 16:05:25 +10:00
|
|
|
static inline void
|
|
|
|
|
draw_tablet(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
|
|
|
|
double x, y;
|
2016-12-05 20:31:31 +10:00
|
|
|
int first, last;
|
|
|
|
|
size_t mask;
|
2019-11-01 08:20:07 +10:00
|
|
|
int rx, ry;
|
|
|
|
|
|
|
|
|
|
/* pressure/distance bars */
|
|
|
|
|
rx = w->width/2 + 100;
|
|
|
|
|
ry = w->height/2 + 50;
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_set_source_rgb(cr, .2, .6, .6);
|
|
|
|
|
cairo_rectangle(cr, rx, ry, 20, 100);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
|
|
|
|
|
if (w->tool.distance > 0) {
|
|
|
|
|
double pos = w->tool.distance * 100;
|
|
|
|
|
cairo_rectangle(cr, rx, ry + 100 - pos, 20, 5);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
}
|
|
|
|
|
if (w->tool.pressure > 0) {
|
|
|
|
|
double pos = w->tool.pressure * 100;
|
|
|
|
|
if (w->tool.is_down)
|
|
|
|
|
cairo_rectangle(cr, rx + 25, ry + 95, 5, 5);
|
|
|
|
|
cairo_rectangle(cr, rx, ry + 100 - pos, 20, pos);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
}
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
|
2015-11-05 14:37:29 +10:00
|
|
|
/* tablet tool, square for prox-in location */
|
|
|
|
|
cairo_save(cr);
|
2019-02-14 13:01:39 +10:00
|
|
|
cairo_set_source_rgb(cr, .2, .6, .6);
|
2015-11-05 14:37:29 +10:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-11 13:39:43 +10:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-05 14:37:29 +10:00
|
|
|
if (w->tool.pressure)
|
2019-02-14 13:01:39 +10:00
|
|
|
cairo_set_source_rgb(cr, .2, .8, .8);
|
2015-11-05 14:37:29 +10:00
|
|
|
|
|
|
|
|
cairo_translate(cr, w->tool.x, w->tool.y);
|
2018-09-13 13:26:22 +10:00
|
|
|
/* scale of 2.5 is large enough to make the marker visible around the
|
|
|
|
|
physical totem */
|
|
|
|
|
cairo_scale(cr,
|
|
|
|
|
1.0 + w->tool.size_major * 2.5,
|
|
|
|
|
1.0 + w->tool.size_minor * 2.5);
|
2016-06-27 11:33:49 +10:00
|
|
|
cairo_scale(cr, 1.0 + w->tool.tilt_x/30.0, 1.0 + w->tool.tilt_y/30.0);
|
2018-09-13 13:26:22 +10:00
|
|
|
if (w->tool.rotation)
|
|
|
|
|
cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
|
|
|
|
|
if (w->tool.pressure)
|
|
|
|
|
cairo_set_source_rgb(cr, .8, .8, .2);
|
2015-11-05 14:37:29 +10:00
|
|
|
cairo_arc(cr, 0, 0,
|
|
|
|
|
1 + 10 * max(w->tool.pressure, w->tool.distance),
|
|
|
|
|
0, 2 * M_PI);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
|
2018-09-13 13:26:22 +10:00
|
|
|
/* The line to indicate the origin */
|
|
|
|
|
if (w->tool.size_major) {
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_scale(cr, 1.0, 1.0);
|
|
|
|
|
cairo_translate(cr, w->tool.x, w->tool.y);
|
|
|
|
|
if (w->tool.rotation)
|
|
|
|
|
cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
|
|
|
|
|
cairo_set_source_rgb(cr, .0, .0, .0);
|
|
|
|
|
cairo_move_to(cr, 0, 0);
|
|
|
|
|
cairo_rel_line_to(cr, 0, -w->tool.size_major * 2.5);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-12 16:39:15 +10:00
|
|
|
/* 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);
|
|
|
|
|
|
2019-02-14 12:57:11 +10:00
|
|
|
for (int i = first + 1; i < last; i++) {
|
2016-01-12 16:39:15 +10:00
|
|
|
x = w->tool.deltas[i % mask].x;
|
|
|
|
|
y = w->tool.deltas[i % mask].y;
|
|
|
|
|
cairo_line_to(cr, x, y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_stroke(cr);
|
2019-02-14 12:44:27 +10:00
|
|
|
cairo_restore(cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
draw_pointer(struct window *w, cairo_t *cr)
|
|
|
|
|
{
|
2019-02-14 12:57:11 +10:00
|
|
|
double x, y;
|
|
|
|
|
int first, last;
|
|
|
|
|
size_t mask;
|
|
|
|
|
|
2016-01-28 16:31:22 +10:00
|
|
|
/* draw pointer sprite */
|
|
|
|
|
cairo_set_source_rgb(cr, 0, 0, 0);
|
|
|
|
|
cairo_save(cr);
|
2021-03-10 14:53:08 +10:00
|
|
|
cairo_move_to(cr, w->pointer.x, w->pointer.y);
|
2016-01-28 16:31:22 +10:00
|
|
|
cairo_rel_line_to(cr, 10, 15);
|
|
|
|
|
cairo_rel_line_to(cr, -10, 0);
|
|
|
|
|
cairo_rel_line_to(cr, 0, -15);
|
|
|
|
|
cairo_fill(cr);
|
2019-02-14 12:57:11 +10:00
|
|
|
|
2021-03-10 15:05:59 +10:00
|
|
|
/* draw unaccelerated sprite */
|
|
|
|
|
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_move_to(cr, w->unaccelerated.x, w->unaccelerated.y);
|
|
|
|
|
cairo_rel_line_to(cr, -5, -10);
|
|
|
|
|
cairo_rel_line_to(cr, 10, 0);
|
|
|
|
|
cairo_rel_line_to(cr, -5, 10);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
2019-02-14 12:57:11 +10:00
|
|
|
/* pointer deltas */
|
|
|
|
|
mask = ARRAY_LENGTH(w->deltas);
|
|
|
|
|
first = max(w->ndeltas + 1, mask) - mask;
|
|
|
|
|
last = w->ndeltas;
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgb(cr, .8, .5, .2);
|
|
|
|
|
|
|
|
|
|
x = w->deltas[first % mask].x;
|
|
|
|
|
y = w->deltas[first % mask].y;
|
|
|
|
|
cairo_move_to(cr, x, y);
|
|
|
|
|
|
|
|
|
|
for (int i = first + 1; i < last; i++) {
|
|
|
|
|
x = w->deltas[i % mask].x;
|
|
|
|
|
y = w->deltas[i % mask].y;
|
|
|
|
|
cairo_line_to(cr, x, y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_stroke(cr);
|
2016-01-28 16:31:22 +10:00
|
|
|
cairo_restore(cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
}
|
|
|
|
|
|
2016-04-19 11:04:26 +10:00
|
|
|
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);
|
2018-04-10 11:05:52 +10:00
|
|
|
|
|
|
|
|
/* round targets */
|
|
|
|
|
for (int i = 0; i <= 3; i++) {
|
|
|
|
|
x1 = w->width * i/4.0;
|
|
|
|
|
x2 = w->width * i/4.0;
|
|
|
|
|
|
|
|
|
|
y1 = w->height * 1.0/4.0;
|
|
|
|
|
y2 = w->height * 3.0/4.0;
|
|
|
|
|
|
|
|
|
|
cairo_arc(cr, x1, y1, 10, 0, 2 * M_PI);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
cairo_arc(cr, x2, y2, 10, 0, 2 * M_PI);
|
|
|
|
|
cairo_stroke(cr);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-14 12:44:27 +10:00
|
|
|
cairo_restore(cr);
|
2016-04-19 11:04:26 +10:00
|
|
|
}
|
|
|
|
|
|
2016-01-29 16:05:25 +10:00
|
|
|
static gboolean
|
|
|
|
|
draw(GtkWidget *widget, cairo_t *cr, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
struct window *w = data;
|
|
|
|
|
|
2023-10-16 04:31:17 +00:00
|
|
|
cairo_set_font_size(cr, 12.0);
|
2016-01-29 16:05:25 +10:00
|
|
|
cairo_set_source_rgb(cr, 1, 1, 1);
|
|
|
|
|
cairo_rectangle(cr, 0, 0, w->width, w->height);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
|
2016-04-19 11:04:26 +10:00
|
|
|
draw_background(w, cr);
|
2018-11-07 11:05:05 +10:00
|
|
|
draw_evdev_rel(w, cr);
|
|
|
|
|
draw_evdev_abs(w, cr);
|
2016-04-19 11:04:26 +10:00
|
|
|
|
2019-06-13 10:35:13 +10:00
|
|
|
draw_pad(w, cr);
|
2019-02-13 10:15:29 +10:00
|
|
|
draw_tablet(w, cr);
|
2016-01-29 16:05:25 +10:00
|
|
|
draw_gestures(w, cr);
|
|
|
|
|
draw_scrollbars(w, cr);
|
|
|
|
|
draw_touchpoints(w, cr);
|
|
|
|
|
draw_abs_pointer(w, cr);
|
|
|
|
|
draw_buttons(w, cr);
|
|
|
|
|
draw_pointer(w, cr);
|
2016-01-28 16:31:22 +10:00
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-29 18:07:32 +02:00
|
|
|
#if HAVE_GTK4
|
|
|
|
|
static void
|
|
|
|
|
draw_gtk4(GtkDrawingArea *widget,
|
|
|
|
|
cairo_t *cr,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
gpointer data)
|
|
|
|
|
{
|
|
|
|
|
draw(GTK_WIDGET(widget), cr, data);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
static void
|
2021-07-29 19:33:42 +02:00
|
|
|
window_place_ui_elements(GtkWidget *widget, struct window *w)
|
2014-05-27 15:29:55 +10:00
|
|
|
{
|
2021-07-29 18:07:32 +02:00
|
|
|
#if HAVE_GTK4
|
|
|
|
|
w->width = gtk_widget_get_width(w->area);
|
|
|
|
|
w->height = gtk_widget_get_height(w->area);
|
|
|
|
|
#else
|
2014-05-27 15:29:55 +10:00
|
|
|
gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
|
2021-07-29 18:07:32 +02:00
|
|
|
#endif
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-03-10 14:53:08 +10:00
|
|
|
w->pointer.x = w->width/2;
|
|
|
|
|
w->pointer.y = w->height/2;
|
2021-03-10 15:05:59 +10:00
|
|
|
w->unaccelerated.x = w->width/2;
|
|
|
|
|
w->unaccelerated.y = w->height/2;
|
2021-03-10 14:53:08 +10:00
|
|
|
w->deltas[0].x = w->pointer.x;
|
|
|
|
|
w->deltas[0].y = w->pointer.y;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2018-11-28 12:11:08 +10:00
|
|
|
w->scroll.vx = w->width/2;
|
|
|
|
|
w->scroll.vy = w->height/2;
|
|
|
|
|
w->scroll.hx = w->width/2;
|
|
|
|
|
w->scroll.hy = w->height/2;
|
2018-11-28 12:16:51 +10:00
|
|
|
w->scroll.vx_discrete = w->width/2;
|
|
|
|
|
w->scroll.vy_discrete = w->height/2;
|
|
|
|
|
w->scroll.hx_discrete = w->width/2;
|
|
|
|
|
w->scroll.hy_discrete = w->height/2;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2015-05-22 10:40:16 +10:00
|
|
|
w->swipe.x = w->width/2;
|
|
|
|
|
w->swipe.y = w->height/2;
|
|
|
|
|
|
2015-05-22 11:14:41 +10:00
|
|
|
w->pinch.scale = 1.0;
|
|
|
|
|
w->pinch.x = w->width/2;
|
|
|
|
|
w->pinch.y = w->height/2;
|
2021-07-29 19:33:42 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-29 18:07:32 +02:00
|
|
|
#if HAVE_GTK4
|
|
|
|
|
static void
|
|
|
|
|
map_event_cb(GtkDrawingArea *widget, int width, int height, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
struct window *w = data;
|
|
|
|
|
|
|
|
|
|
window_place_ui_elements(GTK_WIDGET(widget), w);
|
|
|
|
|
|
|
|
|
|
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(w->area),
|
|
|
|
|
draw_gtk4,
|
|
|
|
|
w,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
gtk_widget_set_cursor_from_name(w->win, "none");
|
2021-06-16 22:53:45 +02:00
|
|
|
|
|
|
|
|
window_lock_pointer(w);
|
2021-07-29 18:07:32 +02:00
|
|
|
}
|
|
|
|
|
#else
|
2021-07-29 19:33:42 +02:00
|
|
|
static void
|
|
|
|
|
map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
struct window *w = data;
|
|
|
|
|
GdkDisplay *display;
|
|
|
|
|
GdkWindow *window;
|
|
|
|
|
|
|
|
|
|
window_place_ui_elements(widget, w);
|
2015-05-22 11:14:41 +10:00
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
|
|
|
|
|
|
2015-03-18 09:44:09 +10:00
|
|
|
window = gdk_event_get_window(event);
|
|
|
|
|
display = gdk_window_get_display(window);
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
gdk_window_set_cursor(gtk_widget_get_window(w->win),
|
2015-03-18 09:44:09 +10:00
|
|
|
gdk_cursor_new_for_display(display,
|
|
|
|
|
GDK_BLANK_CURSOR));
|
2017-06-13 15:18:56 +10:00
|
|
|
|
2021-06-16 22:53:45 +02:00
|
|
|
window_lock_pointer(w);
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
2021-07-29 18:07:32 +02:00
|
|
|
#endif
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-06-10 20:06:23 +02:00
|
|
|
static void
|
|
|
|
|
window_quit(struct window *w)
|
|
|
|
|
{
|
2021-06-10 20:07:00 +02:00
|
|
|
g_main_loop_quit(w->event_loop);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-29 18:07:32 +02:00
|
|
|
#if HAVE_GTK4
|
|
|
|
|
static gboolean
|
|
|
|
|
window_delete_event_cb(GtkWindow *window, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
struct window *w = data;
|
|
|
|
|
|
|
|
|
|
window_quit(w);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
#else
|
2021-06-10 20:07:00 +02:00
|
|
|
static void
|
|
|
|
|
window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
struct window *w = data;
|
|
|
|
|
|
|
|
|
|
window_quit(w);
|
2021-06-10 20:06:23 +02:00
|
|
|
}
|
2021-07-29 18:07:32 +02:00
|
|
|
#endif
|
2021-06-10 20:06:23 +02:00
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
static void
|
|
|
|
|
window_init(struct window *w)
|
|
|
|
|
{
|
2018-11-07 11:05:05 +10:00
|
|
|
list_init(&w->evdev_devices);
|
|
|
|
|
|
2021-07-29 18:07:32 +02:00
|
|
|
#if HAVE_GTK4
|
|
|
|
|
w->win = gtk_window_new();
|
|
|
|
|
#else
|
2014-05-27 15:29:55 +10:00
|
|
|
w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
2021-07-29 18:07:32 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (getenv("LIBINPUT_RUNNING_TEST_SUITE")) {
|
|
|
|
|
#if HAVE_GTK4
|
|
|
|
|
gtk_window_minimize(GTK_WINDOW(w->win));
|
|
|
|
|
#else
|
2020-07-03 09:55:37 +10:00
|
|
|
gtk_window_iconify(GTK_WINDOW(w->win));
|
2021-07-29 18:07:32 +02:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
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);
|
2021-07-29 18:07:32 +02:00
|
|
|
|
|
|
|
|
w->area = gtk_drawing_area_new();
|
|
|
|
|
|
|
|
|
|
#if HAVE_GTK4
|
|
|
|
|
g_signal_connect(G_OBJECT(w->area), "resize", G_CALLBACK(map_event_cb), w);
|
|
|
|
|
g_signal_connect(G_OBJECT(w->win), "close-request", G_CALLBACK(window_delete_event_cb), w);
|
|
|
|
|
|
|
|
|
|
gtk_window_set_child(GTK_WINDOW(w->win), w->area);
|
2023-04-20 09:50:37 +10:00
|
|
|
gtk_widget_set_visible(w->win, TRUE);
|
2021-07-29 18:07:32 +02:00
|
|
|
#else
|
2014-05-27 15:29:55 +10:00
|
|
|
g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
|
2021-06-10 20:07:00 +02:00
|
|
|
g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(window_delete_event_cb), w);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-07-29 18:07:32 +02:00
|
|
|
gtk_widget_set_events(w->win, 0);
|
2014-05-27 15:29:55 +10:00
|
|
|
gtk_widget_set_events(w->area, 0);
|
2021-07-29 18:07:32 +02:00
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
gtk_container_add(GTK_CONTAINER(w->win), w->area);
|
|
|
|
|
gtk_widget_show_all(w->win);
|
2021-07-29 18:07:32 +02:00
|
|
|
#endif
|
2019-06-13 10:35:13 +10:00
|
|
|
|
|
|
|
|
w->pad.ring.position = -1;
|
|
|
|
|
w->pad.strip.position = -1;
|
2024-01-30 14:43:59 +10:00
|
|
|
w->pad.dial.position = -1;
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
|
2014-07-04 08:56:44 +10:00
|
|
|
static void
|
|
|
|
|
window_cleanup(struct window *w)
|
|
|
|
|
{
|
|
|
|
|
ARRAY_FOR_EACH(w->devices, dev) {
|
|
|
|
|
if (*dev)
|
|
|
|
|
libinput_device_unref(*dev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-04 09:04:10 +10:00
|
|
|
static void
|
|
|
|
|
change_ptraccel(struct window *w, double amount)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-07 11:05:05 +10:00
|
|
|
static int
|
|
|
|
|
handle_event_evdev(GIOChannel *source, GIOCondition condition, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_device *dev = data;
|
|
|
|
|
struct libinput *li = libinput_device_get_context(dev);
|
|
|
|
|
struct window *w = libinput_get_user_data(li);
|
|
|
|
|
struct evdev_device *d,
|
|
|
|
|
*device = NULL;
|
|
|
|
|
struct input_event e;
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
list_for_each(d, &w->evdev_devices, node) {
|
|
|
|
|
if (d->libinput_device == dev) {
|
|
|
|
|
device = d;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (device == NULL) {
|
|
|
|
|
msg("Unknown device: %s\n", libinput_device_get_name(dev));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
rc = libevdev_next_event(device->evdev,
|
|
|
|
|
LIBEVDEV_READ_FLAG_NORMAL,
|
|
|
|
|
&e);
|
|
|
|
|
if (rc == -EAGAIN) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (rc == LIBEVDEV_READ_STATUS_SYNC) {
|
|
|
|
|
msg("SYN_DROPPED received\n");
|
|
|
|
|
goto out;
|
|
|
|
|
} else if (rc != LIBEVDEV_READ_STATUS_SUCCESS) {
|
|
|
|
|
msg("Error reading event: %s\n", strerror(-rc));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define EVENT(t_, c_) (t_ << 16 | c_)
|
|
|
|
|
switch (EVENT(e.type, e.code)) {
|
|
|
|
|
case EVENT(EV_REL, REL_X):
|
|
|
|
|
w->evdev.rel_x = e.value;
|
|
|
|
|
break;
|
|
|
|
|
case EVENT(EV_REL, REL_Y):
|
|
|
|
|
w->evdev.rel_y = e.value;
|
|
|
|
|
break;
|
|
|
|
|
case EVENT(EV_ABS, ABS_MT_SLOT):
|
|
|
|
|
w->evdev.slot = min((unsigned int)e.value,
|
|
|
|
|
ARRAY_LENGTH(w->evdev.slots) - 1);
|
|
|
|
|
w->evdev.device = (uintptr_t)dev;
|
|
|
|
|
break;
|
|
|
|
|
case EVENT(EV_ABS, ABS_MT_TRACKING_ID):
|
|
|
|
|
w->evdev.slots[w->evdev.slot].active = (e.value != -1);
|
|
|
|
|
w->evdev.device = (uintptr_t)dev;
|
|
|
|
|
break;
|
|
|
|
|
case EVENT(EV_ABS, ABS_X):
|
|
|
|
|
w->evdev.x = e.value;
|
|
|
|
|
w->evdev.device = (uintptr_t)dev;
|
|
|
|
|
break;
|
|
|
|
|
case EVENT(EV_ABS, ABS_Y):
|
|
|
|
|
w->evdev.y = e.value;
|
|
|
|
|
w->evdev.device = (uintptr_t)dev;
|
|
|
|
|
break;
|
|
|
|
|
case EVENT(EV_ABS, ABS_MT_POSITION_X):
|
|
|
|
|
w->evdev.slots[w->evdev.slot].x = e.value;
|
|
|
|
|
w->evdev.device = (uintptr_t)dev;
|
|
|
|
|
break;
|
|
|
|
|
case EVENT(EV_ABS, ABS_MT_POSITION_Y):
|
|
|
|
|
w->evdev.slots[w->evdev.slot].y = e.value;
|
|
|
|
|
w->evdev.device = (uintptr_t)dev;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (rc == LIBEVDEV_READ_STATUS_SUCCESS);
|
|
|
|
|
|
|
|
|
|
gtk_widget_queue_draw(w->area);
|
|
|
|
|
out:
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
register_evdev_device(struct window *w, struct libinput_device *dev)
|
|
|
|
|
{
|
|
|
|
|
GIOChannel *c;
|
|
|
|
|
struct udev_device *ud;
|
|
|
|
|
struct libevdev *evdev;
|
|
|
|
|
const char *device_node;
|
|
|
|
|
int fd;
|
|
|
|
|
struct evdev_device *d;
|
|
|
|
|
|
|
|
|
|
ud = libinput_device_get_udev_device(dev);
|
|
|
|
|
device_node = udev_device_get_devnode(ud);
|
|
|
|
|
|
|
|
|
|
fd = open(device_node, O_RDONLY|O_NONBLOCK);
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
msg("failed to open %s, evdev events unavailable\n", device_node);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (libevdev_new_from_fd(fd, &evdev) != 0) {
|
|
|
|
|
msg("failed to create context for %s, evdev events unavailable\n",
|
|
|
|
|
device_node);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d = zalloc(sizeof *d);
|
|
|
|
|
list_append(&w->evdev_devices, &d->node);
|
|
|
|
|
d->fd = fd;
|
|
|
|
|
d->evdev = evdev;
|
|
|
|
|
d->libinput_device =libinput_device_ref(dev);
|
|
|
|
|
|
|
|
|
|
c = g_io_channel_unix_new(fd);
|
|
|
|
|
g_io_channel_set_encoding(c, NULL, NULL);
|
|
|
|
|
d->source_id = g_io_add_watch(c, G_IO_IN,
|
|
|
|
|
handle_event_evdev,
|
|
|
|
|
d->libinput_device);
|
|
|
|
|
fd = -1;
|
|
|
|
|
out:
|
|
|
|
|
close(fd);
|
|
|
|
|
udev_device_unref(ud);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
unregister_evdev_device(struct window *w, struct libinput_device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *d;
|
|
|
|
|
|
2021-05-19 21:48:34 +09:00
|
|
|
list_for_each_safe(d, &w->evdev_devices, node) {
|
2018-11-07 11:05:05 +10:00
|
|
|
if (d->libinput_device != dev)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
list_remove(&d->node);
|
|
|
|
|
g_source_remove(d->source_id);
|
2018-11-28 12:16:51 +10:00
|
|
|
free(libinput_device_get_user_data(d->libinput_device));
|
2018-11-07 11:05:05 +10:00
|
|
|
libinput_device_unref(d->libinput_device);
|
|
|
|
|
libevdev_free(d->evdev);
|
|
|
|
|
close(d->fd);
|
|
|
|
|
free(d);
|
|
|
|
|
w->evdev.last_device = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
static void
|
|
|
|
|
handle_event_device_notify(struct libinput_event *ev)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_device *dev = libinput_event_get_device(ev);
|
2014-07-04 08:56:44 +10:00
|
|
|
struct libinput *li;
|
|
|
|
|
struct window *w;
|
2014-05-27 15:29:55 +10:00
|
|
|
const char *type;
|
|
|
|
|
|
2018-11-07 11:05:05 +10:00
|
|
|
li = libinput_event_get_context(ev);
|
|
|
|
|
w = libinput_get_user_data(li);
|
|
|
|
|
|
|
|
|
|
if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
|
2014-05-27 15:29:55 +10:00
|
|
|
type = "added";
|
2018-11-07 11:05:05 +10:00
|
|
|
register_evdev_device(w, dev);
|
2018-11-29 09:15:52 +10:00
|
|
|
tools_device_apply_config(libinput_event_get_device(ev),
|
|
|
|
|
&w->options);
|
2018-11-07 11:05:05 +10:00
|
|
|
} else {
|
2014-05-27 15:29:55 +10:00
|
|
|
type = "removed";
|
2018-11-07 11:05:05 +10:00
|
|
|
unregister_evdev_device(w, dev);
|
|
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2014-11-18 11:33:51 +10:00
|
|
|
msg("%s %-30s %s\n",
|
|
|
|
|
libinput_device_get_sysname(dev),
|
|
|
|
|
libinput_device_get_name(dev),
|
|
|
|
|
type);
|
2014-07-21 10:58:24 +10:00
|
|
|
|
2014-07-04 08:56:44 +10:00
|
|
|
if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
|
2021-05-19 21:09:30 +09:00
|
|
|
ARRAY_FOR_EACH(w->devices, device) {
|
|
|
|
|
if (*device == NULL) {
|
|
|
|
|
*device = libinput_device_ref(dev);
|
2014-07-04 08:56:44 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-05-19 21:09:30 +09:00
|
|
|
ARRAY_FOR_EACH(w->devices, device) {
|
|
|
|
|
if (*device == dev) {
|
|
|
|
|
libinput_device_unref(*device);
|
|
|
|
|
*device = NULL;
|
2014-07-04 08:56:44 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2021-03-10 15:05:59 +10:00
|
|
|
double dx_unaccel = libinput_event_pointer_get_dx_unaccelerated(p),
|
|
|
|
|
dy_unaccel = libinput_event_pointer_get_dy_unaccelerated(p);
|
2018-08-07 12:42:40 +10:00
|
|
|
struct point point;
|
|
|
|
|
const int mask = ARRAY_LENGTH(w->deltas);
|
|
|
|
|
size_t idx;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-03-10 15:05:59 +10:00
|
|
|
w->pointer.x = clip(w->pointer.x + dx, 0.0, w->width);
|
|
|
|
|
w->pointer.y = clip(w->pointer.y + dy, 0.0, w->height);
|
|
|
|
|
w->unaccelerated.x = clip(w->unaccelerated.x + dx_unaccel, 0.0, w->width);
|
|
|
|
|
w->unaccelerated.y = clip(w->unaccelerated.y + dy_unaccel, 0.0, w->height);
|
2018-08-07 12:42:40 +10:00
|
|
|
|
|
|
|
|
idx = w->ndeltas % mask;
|
|
|
|
|
point = w->deltas[idx];
|
|
|
|
|
idx = (w->ndeltas + 1) % mask;
|
2021-03-10 15:05:59 +10:00
|
|
|
point.x += dx_unaccel;
|
|
|
|
|
point.y += dy_unaccel;
|
2018-08-07 12:42:40 +10:00
|
|
|
w->deltas[idx] = point;
|
|
|
|
|
w->ndeltas++;
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_event_absmotion(struct libinput_event *ev, struct window *w)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
|
2014-07-03 09:57:12 +10:00
|
|
|
double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
|
|
|
|
|
y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-03-10 14:54:19 +10:00
|
|
|
w->abs.x = x;
|
|
|
|
|
w->abs.y = y;
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2014-07-14 00:15:07 +02:00
|
|
|
if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches))
|
2014-05-27 15:29:55 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
touch = &w->touches[slot];
|
|
|
|
|
|
2019-02-07 09:30:46 +10:00
|
|
|
switch (libinput_event_get_type(ev)) {
|
|
|
|
|
case LIBINPUT_EVENT_TOUCH_UP:
|
|
|
|
|
touch->state = TOUCH_ENDED;
|
2014-05-27 15:29:55 +10:00
|
|
|
return;
|
2019-02-07 09:30:46 +10:00
|
|
|
case LIBINPUT_EVENT_TOUCH_CANCEL:
|
|
|
|
|
touch->state = TOUCH_CANCELLED;
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
|
2014-07-03 09:57:12 +10:00
|
|
|
x = libinput_event_touch_get_x_transformed(t, w->width),
|
|
|
|
|
y = libinput_event_touch_get_y_transformed(t, w->height);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2019-02-07 09:30:46 +10:00
|
|
|
touch->state = TOUCH_ACTIVE;
|
2014-05-27 15:29:55 +10:00
|
|
|
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);
|
2015-01-23 22:31:05 +01:00
|
|
|
double value;
|
2018-11-22 10:24:54 +10:00
|
|
|
enum libinput_pointer_axis axis;
|
|
|
|
|
enum libinput_event_type type;
|
2018-11-28 12:16:51 +10:00
|
|
|
|
2018-11-22 10:24:54 +10:00
|
|
|
type = libinput_event_get_type(ev);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2018-11-22 10:24:54 +10:00
|
|
|
axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
|
|
|
|
|
if (libinput_event_pointer_has_axis(p, axis)) {
|
|
|
|
|
value = libinput_event_pointer_get_scroll_value(p, axis);
|
2018-11-28 12:11:08 +10:00
|
|
|
w->scroll.vy += value;
|
|
|
|
|
w->scroll.vy = clip(w->scroll.vy, 0, w->height);
|
2018-11-28 12:16:51 +10:00
|
|
|
|
2018-11-22 10:24:54 +10:00
|
|
|
if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
|
|
|
|
|
w->scroll.vy_discrete += value;
|
2018-11-28 12:16:51 +10:00
|
|
|
w->scroll.vy_discrete = clip(w->scroll.vy_discrete, 0, w->height);
|
|
|
|
|
}
|
2014-12-24 11:10:04 +10:00
|
|
|
}
|
2015-01-23 22:31:05 +01:00
|
|
|
|
2018-11-22 10:24:54 +10:00
|
|
|
axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
|
|
|
|
|
if (libinput_event_pointer_has_axis(p, axis)) {
|
|
|
|
|
value = libinput_event_pointer_get_scroll_value(p, axis);
|
2018-11-28 12:11:08 +10:00
|
|
|
w->scroll.hx += value;
|
|
|
|
|
w->scroll.hx = clip(w->scroll.hx, 0, w->width);
|
2018-11-28 12:16:51 +10:00
|
|
|
|
2018-11-22 10:24:54 +10:00
|
|
|
if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
|
|
|
|
|
w->scroll.hx_discrete += value;
|
2018-11-28 12:16:51 +10:00
|
|
|
w->scroll.hx_discrete = clip(w->scroll.hx_discrete, 0, w->width);
|
|
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
handle_event_keyboard(struct libinput_event *ev, struct window *w)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
|
2014-07-04 09:04:10 +10:00
|
|
|
unsigned int key = libinput_event_keyboard_get_key(k);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2014-07-04 09:04:10 +10:00
|
|
|
if (libinput_event_keyboard_get_key_state(k) ==
|
|
|
|
|
LIBINPUT_KEY_STATE_RELEASED)
|
|
|
|
|
return 0;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2014-07-04 09:04:10 +10:00
|
|
|
switch(key) {
|
|
|
|
|
case KEY_ESC:
|
2014-05-27 15:29:55 +10:00
|
|
|
return 1;
|
2014-07-04 09:04:10 +10:00
|
|
|
case KEY_UP:
|
|
|
|
|
change_ptraccel(w, 0.1);
|
|
|
|
|
break;
|
|
|
|
|
case KEY_DOWN:
|
|
|
|
|
change_ptraccel(w, -0.1);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
|
|
|
|
|
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);
|
2019-02-12 10:05:25 +10:00
|
|
|
bool is_press;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
|
|
|
|
is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
|
|
|
|
|
|
|
|
|
|
switch (button) {
|
|
|
|
|
case BTN_LEFT:
|
2019-02-12 10:05:25 +10:00
|
|
|
w->buttons.l = is_press;
|
2014-05-27 15:29:55 +10:00
|
|
|
break;
|
|
|
|
|
case BTN_RIGHT:
|
2019-02-12 10:05:25 +10:00
|
|
|
w->buttons.r = is_press;
|
2014-05-27 15:29:55 +10:00
|
|
|
break;
|
|
|
|
|
case BTN_MIDDLE:
|
2019-02-12 10:05:25 +10:00
|
|
|
w->buttons.m = is_press;
|
2014-05-27 15:29:55 +10:00
|
|
|
break;
|
2019-02-12 14:46:58 +10:00
|
|
|
default:
|
|
|
|
|
w->buttons.other = is_press;
|
|
|
|
|
w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
|
|
|
|
|
button);
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-22 10:40:16 +10:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-22 11:14:41 +10:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 19:19:38 +02:00
|
|
|
static void
|
|
|
|
|
handle_event_hold(struct libinput_event *ev, struct window *w)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
|
|
|
|
|
int nfingers;
|
|
|
|
|
|
|
|
|
|
nfingers = libinput_event_gesture_get_finger_count(g);
|
|
|
|
|
|
|
|
|
|
switch (libinput_event_get_type(ev)) {
|
|
|
|
|
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
|
|
|
|
|
w->hold.nfingers = nfingers;
|
|
|
|
|
w->hold.active = true;
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_EVENT_GESTURE_HOLD_END:
|
|
|
|
|
w->hold.nfingers = nfingers;
|
|
|
|
|
w->hold.active = false;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-05 14:37:29 +10:00
|
|
|
static void
|
|
|
|
|
handle_event_tablet(struct libinput_event *ev, struct window *w)
|
|
|
|
|
{
|
2015-11-16 16:28:55 +10:00
|
|
|
struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
|
2024-06-07 12:18:40 +10:00
|
|
|
struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(t);
|
2015-11-11 13:39:43 +10:00
|
|
|
double x, y;
|
2016-01-12 16:39:15 +10:00
|
|
|
struct point point;
|
|
|
|
|
int idx;
|
|
|
|
|
const int mask = ARRAY_LENGTH(w->tool.deltas);
|
2019-06-13 09:48:28 +10:00
|
|
|
bool is_press;
|
|
|
|
|
unsigned int button;
|
2015-11-05 14:37:29 +10:00
|
|
|
|
2016-01-12 14:41:57 +10:00
|
|
|
x = libinput_event_tablet_tool_get_x_transformed(t, w->width);
|
|
|
|
|
y = libinput_event_tablet_tool_get_y_transformed(t, w->height);
|
|
|
|
|
|
2015-11-05 14:37:29 +10:00
|
|
|
switch (libinput_event_get_type(ev)) {
|
2015-11-16 16:27:46 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
|
2024-06-07 12:18:40 +10:00
|
|
|
tools_tablet_tool_apply_config(tool, &w->options);
|
2015-11-16 16:28:55 +10:00
|
|
|
if (libinput_event_tablet_tool_get_proximity_state(t) ==
|
2016-01-05 14:08:01 +10:00
|
|
|
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
|
2015-11-05 14:37:29 +10:00
|
|
|
w->tool.x_in = 0;
|
|
|
|
|
w->tool.y_in = 0;
|
2015-11-11 13:39:43 +10:00
|
|
|
w->tool.x_down = 0;
|
|
|
|
|
w->tool.y_down = 0;
|
|
|
|
|
w->tool.x_up = 0;
|
|
|
|
|
w->tool.y_up = 0;
|
2015-11-05 14:37:29 +10:00
|
|
|
} else {
|
2016-01-12 14:41:57 +10:00
|
|
|
w->tool.x_in = x;
|
|
|
|
|
w->tool.y_in = y;
|
2016-01-12 16:39:15 +10:00
|
|
|
w->tool.ndeltas = 0;
|
|
|
|
|
w->tool.deltas[0].x = w->width/2;
|
|
|
|
|
w->tool.deltas[0].y = w->height/2;
|
2015-11-05 14:37:29 +10:00
|
|
|
}
|
|
|
|
|
break;
|
2015-11-16 16:27:46 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_TOOL_TIP:
|
2016-01-12 14:40:27 +10:00
|
|
|
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);
|
2015-11-16 16:28:55 +10:00
|
|
|
if (libinput_event_tablet_tool_get_tip_state(t) ==
|
2015-11-16 15:47:15 +10:00
|
|
|
LIBINPUT_TABLET_TOOL_TIP_DOWN) {
|
2015-11-11 13:39:43 +10:00
|
|
|
w->tool.x_down = x;
|
|
|
|
|
w->tool.y_down = y;
|
2019-11-01 08:20:07 +10:00
|
|
|
w->tool.is_down = true;
|
2015-11-11 13:39:43 +10:00
|
|
|
} else {
|
|
|
|
|
w->tool.x_up = x;
|
|
|
|
|
w->tool.y_up = y;
|
2019-11-01 08:20:07 +10:00
|
|
|
w->tool.is_down = false;
|
2015-11-11 13:39:43 +10:00
|
|
|
}
|
2021-07-22 15:00:32 +10:00
|
|
|
_fallthrough_;
|
2016-01-12 14:41:57 +10:00
|
|
|
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);
|
2018-09-13 13:26:22 +10:00
|
|
|
w->tool.rotation = libinput_event_tablet_tool_get_rotation(t);
|
|
|
|
|
w->tool.size_major = libinput_event_tablet_tool_get_size_major(t);
|
|
|
|
|
w->tool.size_minor = libinput_event_tablet_tool_get_size_minor(t);
|
2016-01-12 16:39:15 +10:00
|
|
|
|
|
|
|
|
/* 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++;
|
2015-11-11 13:39:43 +10:00
|
|
|
break;
|
2015-11-16 16:27:46 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
|
2019-06-13 09:48:28 +10:00
|
|
|
is_press = libinput_event_tablet_tool_get_button_state(t) == LIBINPUT_BUTTON_STATE_PRESSED;
|
|
|
|
|
button = libinput_event_tablet_tool_get_button(t);
|
|
|
|
|
|
|
|
|
|
w->buttons.other = is_press;
|
|
|
|
|
w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
|
|
|
|
|
button);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_event_tablet_pad(struct libinput_event *ev, struct window *w)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
|
|
|
|
|
bool is_press;
|
|
|
|
|
unsigned int button;
|
|
|
|
|
static const char *pad_buttons[] = {
|
|
|
|
|
"Pad 0", "Pad 1", "Pad 2", "Pad 3", "Pad 4", "Pad 5",
|
|
|
|
|
"Pad 6", "Pad 7", "Pad 8", "Pad 9", "Pad >= 10"
|
|
|
|
|
};
|
2024-01-30 14:43:59 +10:00
|
|
|
double position, delta;
|
2019-06-13 10:35:13 +10:00
|
|
|
double number;
|
2019-06-13 09:48:28 +10:00
|
|
|
|
|
|
|
|
switch (libinput_event_get_type(ev)) {
|
|
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
|
|
|
|
|
is_press = libinput_event_tablet_pad_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
|
|
|
|
|
button = libinput_event_tablet_pad_get_button_number(p);
|
|
|
|
|
w->buttons.other = is_press;
|
|
|
|
|
w->buttons.other_name = pad_buttons[min(button, 10)];
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_RING:
|
2019-06-13 10:35:13 +10:00
|
|
|
position = libinput_event_tablet_pad_get_ring_position(p);
|
|
|
|
|
number = libinput_event_tablet_pad_get_ring_number(p);
|
|
|
|
|
w->pad.ring.number = number;
|
|
|
|
|
w->pad.ring.position = position;
|
|
|
|
|
break;
|
2019-06-13 09:48:28 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
|
2019-06-13 10:35:13 +10:00
|
|
|
position = libinput_event_tablet_pad_get_strip_position(p);
|
|
|
|
|
number = libinput_event_tablet_pad_get_strip_number(p);
|
|
|
|
|
w->pad.strip.number = number;
|
|
|
|
|
w->pad.strip.position = position;
|
2015-11-05 14:37:29 +10:00
|
|
|
break;
|
2024-01-30 14:43:59 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_DIAL:
|
|
|
|
|
delta = libinput_event_tablet_pad_get_dial_delta_v120(p);
|
|
|
|
|
number = libinput_event_tablet_pad_get_dial_number(p);
|
|
|
|
|
if (w->pad.dial.number != number)
|
|
|
|
|
w->pad.dial.position = -delta;
|
|
|
|
|
w->pad.dial.number = number;
|
|
|
|
|
w->pad.dial.position += delta;
|
|
|
|
|
break;
|
2015-11-05 14:37:29 +10:00
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
static gboolean
|
|
|
|
|
handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
struct libinput *li = data;
|
2017-06-19 18:38:33 +10:00
|
|
|
struct window *w = libinput_get_user_data(li);
|
2014-05-27 15:29:55 +10:00
|
|
|
struct libinput_event *ev;
|
|
|
|
|
|
2020-10-06 15:35:44 +10:00
|
|
|
tools_dispatch(li);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
|
|
|
|
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:
|
2019-02-07 09:30:46 +10:00
|
|
|
case LIBINPUT_EVENT_TOUCH_CANCEL:
|
2014-05-27 15:29:55 +10:00
|
|
|
handle_event_touch(ev, w);
|
|
|
|
|
break;
|
2019-02-07 09:30:46 +10:00
|
|
|
case LIBINPUT_EVENT_TOUCH_FRAME:
|
|
|
|
|
break;
|
2014-05-27 15:29:55 +10:00
|
|
|
case LIBINPUT_EVENT_POINTER_AXIS:
|
2018-11-22 10:24:54 +10:00
|
|
|
/* ignore */
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
|
|
|
|
|
case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
|
|
|
|
|
case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
|
2014-05-27 15:29:55 +10:00
|
|
|
handle_event_axis(ev, w);
|
|
|
|
|
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);
|
2021-06-10 20:06:23 +02:00
|
|
|
window_quit(w);
|
2014-05-27 15:29:55 +10:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2015-01-22 16:41:50 +01:00
|
|
|
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
|
|
|
|
|
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
|
|
|
|
|
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
|
2015-05-22 10:40:16 +10:00
|
|
|
handle_event_swipe(ev, w);
|
|
|
|
|
break;
|
2015-03-04 15:24:04 +01:00
|
|
|
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
|
|
|
|
|
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
|
|
|
|
|
case LIBINPUT_EVENT_GESTURE_PINCH_END:
|
2015-05-22 11:14:41 +10:00
|
|
|
handle_event_pinch(ev, w);
|
2015-01-22 16:41:50 +01:00
|
|
|
break;
|
2021-05-27 19:19:38 +02:00
|
|
|
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
|
|
|
|
|
case LIBINPUT_EVENT_GESTURE_HOLD_END:
|
|
|
|
|
handle_event_hold(ev, w);
|
|
|
|
|
break;
|
2015-11-16 16:27:46 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
|
|
|
|
|
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
|
|
|
|
|
case LIBINPUT_EVENT_TABLET_TOOL_TIP:
|
|
|
|
|
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
|
2015-11-05 14:37:29 +10:00
|
|
|
handle_event_tablet(ev, w);
|
2014-06-25 14:45:08 +10:00
|
|
|
break;
|
2016-01-21 12:35:11 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
|
|
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_RING:
|
|
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
|
2024-01-30 14:43:59 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_DIAL:
|
2019-06-13 09:48:28 +10:00
|
|
|
handle_event_tablet_pad(ev, w);
|
2016-01-21 12:35:11 +10:00
|
|
|
break;
|
2019-01-17 11:08:27 +10:00
|
|
|
case LIBINPUT_EVENT_TABLET_PAD_KEY:
|
|
|
|
|
break;
|
2017-01-20 16:54:13 +11:00
|
|
|
case LIBINPUT_EVENT_SWITCH_TOGGLE:
|
|
|
|
|
break;
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libinput_event_destroy(ev);
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-19 18:38:33 +10:00
|
|
|
static void
|
2025-03-26 11:10:53 +10:00
|
|
|
usage(struct option *opts) {
|
2018-10-31 15:52:25 +10:00
|
|
|
printf("Usage: libinput debug-gui [options] [--udev <seat>|[--device] /dev/input/event0]\n");
|
2025-03-26 11:10:53 +10:00
|
|
|
|
|
|
|
|
if (opts)
|
|
|
|
|
tools_print_usage_option_list(opts);
|
2017-06-19 18:38:33 +10:00
|
|
|
}
|
|
|
|
|
|
2018-11-01 13:56:16 +10:00
|
|
|
static gboolean
|
|
|
|
|
signal_handler(void *data)
|
|
|
|
|
{
|
2021-06-10 20:06:23 +02:00
|
|
|
struct libinput *li = data;
|
|
|
|
|
struct window *w = libinput_get_user_data(li);
|
|
|
|
|
|
|
|
|
|
window_quit(w);
|
2018-11-01 13:56:16 +10:00
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
int
|
2017-05-24 10:36:44 +10:00
|
|
|
main(int argc, char **argv)
|
2014-05-27 15:29:55 +10:00
|
|
|
{
|
2018-08-27 11:14:05 +10:00
|
|
|
struct window w = {0};
|
2017-09-04 15:07:05 +10:00
|
|
|
struct tools_options options;
|
2014-05-27 15:29:55 +10:00
|
|
|
struct libinput *li;
|
2018-10-31 15:52:25 +10:00
|
|
|
enum tools_backend backend = BACKEND_NONE;
|
2020-02-21 10:28:56 +10:00
|
|
|
const char *seat_or_device[2] = {"seat0", NULL};
|
2017-06-19 18:38:33 +10:00
|
|
|
bool verbose = false;
|
2021-07-29 18:07:32 +02:00
|
|
|
bool gtk_init = false;
|
|
|
|
|
|
|
|
|
|
#if HAVE_GTK4
|
|
|
|
|
gtk_init = gtk_init_check();
|
|
|
|
|
#else
|
|
|
|
|
gtk_init = gtk_init_check(&argc, &argv);
|
|
|
|
|
#endif
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-07-29 18:07:32 +02:00
|
|
|
if (!gtk_init)
|
2019-06-26 15:38:43 +10:00
|
|
|
return 77;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2017-09-04 15:07:05 +10:00
|
|
|
tools_init_options(&options);
|
2017-06-19 18:38:33 +10:00
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
int c;
|
|
|
|
|
int option_index = 0;
|
|
|
|
|
enum {
|
|
|
|
|
OPT_DEVICE = 1,
|
|
|
|
|
OPT_UDEV,
|
|
|
|
|
OPT_GRAB,
|
|
|
|
|
OPT_VERBOSE,
|
|
|
|
|
};
|
|
|
|
|
static struct option opts[] = {
|
|
|
|
|
CONFIGURATION_OPTIONS,
|
|
|
|
|
{ "help", no_argument, 0, 'h' },
|
|
|
|
|
{ "device", required_argument, 0, OPT_DEVICE },
|
|
|
|
|
{ "udev", required_argument, 0, OPT_UDEV },
|
|
|
|
|
{ "grab", no_argument, 0, OPT_GRAB },
|
|
|
|
|
{ "verbose", no_argument, 0, OPT_VERBOSE },
|
|
|
|
|
{ 0, 0, 0, 0}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
c = getopt_long(argc, argv, "h", opts, &option_index);
|
|
|
|
|
if (c == -1)
|
|
|
|
|
break;
|
2014-12-18 14:47:54 +10:00
|
|
|
|
2017-06-19 18:38:33 +10:00
|
|
|
switch(c) {
|
|
|
|
|
case '?':
|
2018-11-01 09:39:49 +10:00
|
|
|
exit(EXIT_INVALID_USAGE);
|
2017-06-19 18:38:33 +10:00
|
|
|
break;
|
|
|
|
|
case 'h':
|
2025-03-26 11:10:53 +10:00
|
|
|
usage(opts);
|
2017-06-19 18:38:33 +10:00
|
|
|
exit(0);
|
|
|
|
|
break;
|
|
|
|
|
case OPT_DEVICE:
|
|
|
|
|
backend = BACKEND_DEVICE;
|
2019-11-25 12:22:19 +10:00
|
|
|
seat_or_device[0] = optarg;
|
2017-06-19 18:38:33 +10:00
|
|
|
break;
|
|
|
|
|
case OPT_UDEV:
|
|
|
|
|
backend = BACKEND_UDEV;
|
2019-11-25 12:22:19 +10:00
|
|
|
seat_or_device[0] = optarg;
|
2017-06-19 18:38:33 +10:00
|
|
|
break;
|
|
|
|
|
case OPT_GRAB:
|
2018-08-27 11:14:05 +10:00
|
|
|
w.grab = true;
|
2017-06-19 18:38:33 +10:00
|
|
|
break;
|
|
|
|
|
case OPT_VERBOSE:
|
|
|
|
|
verbose = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-09-04 15:07:05 +10:00
|
|
|
if (tools_parse_option(c, optarg, &options) != 0) {
|
2025-03-26 11:10:53 +10:00
|
|
|
usage(NULL);
|
2018-11-01 09:39:49 +10:00
|
|
|
return EXIT_INVALID_USAGE;
|
2017-06-19 18:38:33 +10:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2017-06-19 18:38:33 +10:00
|
|
|
if (optind < argc) {
|
2018-10-31 15:52:25 +10:00
|
|
|
if (optind < argc - 1 || backend != BACKEND_NONE) {
|
2025-03-26 11:10:53 +10:00
|
|
|
usage(NULL);
|
2018-10-31 15:52:25 +10:00
|
|
|
return EXIT_INVALID_USAGE;
|
|
|
|
|
}
|
|
|
|
|
backend = BACKEND_DEVICE;
|
2019-11-25 12:22:19 +10:00
|
|
|
seat_or_device[0] = argv[optind];
|
2018-10-31 15:52:25 +10:00
|
|
|
} else if (backend == BACKEND_NONE) {
|
|
|
|
|
backend = BACKEND_UDEV;
|
2017-06-19 18:38:33 +10:00
|
|
|
}
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2018-08-27 11:14:05 +10:00
|
|
|
li = tools_open_backend(backend, seat_or_device, verbose, &w.grab);
|
2014-12-18 14:47:54 +10:00
|
|
|
if (!li)
|
2018-11-01 09:39:49 +10:00
|
|
|
return EXIT_FAILURE;
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2017-06-19 18:38:33 +10:00
|
|
|
libinput_set_user_data(li, &w);
|
|
|
|
|
|
2021-06-10 20:06:23 +02:00
|
|
|
g_unix_signal_add(SIGINT, signal_handler, li);
|
|
|
|
|
|
2014-05-27 15:29:55 +10:00
|
|
|
window_init(&w);
|
2017-09-04 15:07:05 +10:00
|
|
|
w.options = options;
|
2014-05-27 15:29:55 +10:00
|
|
|
sockets_init(li);
|
2015-07-21 11:53:57 +10:00
|
|
|
handle_event_libinput(NULL, 0, li);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-06-10 20:07:00 +02:00
|
|
|
w.event_loop = g_main_loop_new(NULL, FALSE);
|
|
|
|
|
g_main_loop_run(w.event_loop);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2021-06-16 22:53:45 +02:00
|
|
|
window_unlock_pointer(&w);
|
2014-07-04 08:56:44 +10:00
|
|
|
window_cleanup(&w);
|
2014-06-25 00:06:58 +02:00
|
|
|
libinput_unref(li);
|
2014-05-27 15:29:55 +10:00
|
|
|
|
2018-11-01 09:39:49 +10:00
|
|
|
return EXIT_SUCCESS;
|
2014-05-27 15:29:55 +10:00
|
|
|
}
|