Add "absolute" mouse support

This commit is contained in:
Zoran Vuckovic 2017-10-05 05:18:53 +02:00
parent 032109512a
commit 135b636bd4

View File

@ -1,7 +1,7 @@
/* RetroArch - A frontend for libretro. /* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2015 - Hans-Kristian Arntzen * Copyright (C) 2010-2015 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2011-2017 - Daniel De Matteis
* *
* RetroArch is free software: you can redistribute it and/or modify it under the terms * RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found- * of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version. * ation, either version 3 of the License, or (at your option) any later version.
@ -82,18 +82,17 @@ static const char *g_dev_type_str[] =
typedef struct typedef struct
{ {
int16_t x, y, dlt_x, dlt_y; /* If device is "absolute" coords will be in device specific units
and axis min value will be less than max, otherwise coords will be
relative to full viewport and min and max values will be zero. */
__s32 x_abs, y_abs;
__s32 x_min, y_min;
__s32 x_max, y_max;
__s32 x_rel, y_rel;
bool l, r, m; bool l, r, m;
bool wu, wd, whu, whd; bool wu, wd, whu, whd;
} udev_input_mouse_t; } udev_input_mouse_t;
typedef struct
{
struct input_absinfo info_x;
struct input_absinfo info_y;
udev_input_mouse_t mouse; /* touch-pad will be presented to RA/core as mouse */
} udev_input_touchpad_t;
struct udev_input_device struct udev_input_device
{ {
int fd; int fd;
@ -103,11 +102,7 @@ struct udev_input_device
char devnode[PATH_MAX_LENGTH]; char devnode[PATH_MAX_LENGTH];
enum udev_input_dev_type type; enum udev_input_dev_type type;
union udev_input_mouse_t mouse;
{
udev_input_mouse_t mouse;
udev_input_touchpad_t touchpad;
} state;
}; };
typedef void (*device_handle_cb)(void *data, typedef void (*device_handle_cb)(void *data,
@ -129,6 +124,10 @@ struct udev_input
#ifdef UDEV_XKB_HANDLING #ifdef UDEV_XKB_HANDLING
bool xkb_handling; bool xkb_handling;
#endif #endif
/* OS pointer coords (zeros if we don't have X11) */
int pointer_x;
int pointer_y;
}; };
#ifdef UDEV_XKB_HANDLING #ifdef UDEV_XKB_HANDLING
@ -216,10 +215,7 @@ static udev_input_mouse_t *udev_get_mouse(struct udev_input *udev, unsigned port
if (mouse_index == settings->uints.input_mouse_index[port]) if (mouse_index == settings->uints.input_mouse_index[port])
{ {
if (udev->devices[i]->type == UDEV_INPUT_MOUSE) mouse = &udev->devices[i]->mouse;
mouse = &udev->devices[i]->state.mouse;
else
mouse = &udev->devices[i]->state.touchpad.mouse;
break; break;
} }
@ -229,54 +225,166 @@ static udev_input_mouse_t *udev_get_mouse(struct udev_input *udev, unsigned port
return mouse; return mouse;
} }
static void udev_handle_touchpad(void *data, static void udev_mouse_set_x(udev_input_mouse_t *mouse, __s32 x, bool abs)
const struct input_event *event, udev_input_device_t *dev)
{ {
int16_t pos; video_viewport_t vp;
unsigned width = 0;
unsigned height = 0;
udev_input_touchpad_t *touchpad = &dev->state.touchpad;
udev_input_mouse_t *mouse = &dev->state.touchpad.mouse;
switch (event->type) if (abs)
{ {
case EV_ABS: mouse->x_rel += x - mouse->x_abs;
video_driver_get_size(&width, &height); mouse->x_abs = x;
switch (event->code)
{
case ABS_X:
pos = (float)(event->value - touchpad->info_x.minimum) /
(touchpad->info_x.maximum - touchpad->info_x.minimum) * width;
mouse->dlt_x += pos - mouse->x;
mouse->x = pos;
break;
case ABS_Y:
pos = (float)(event->value - touchpad->info_y.minimum) /
(touchpad->info_y.maximum - touchpad->info_y.minimum) * height;
mouse->dlt_y += pos - mouse->y;
mouse->y = pos;
}
break;
case EV_KEY:
switch (event->code)
{
case BTN_LEFT:
mouse->l = event->value;
break;
case BTN_MIDDLE:
mouse->m = event->value;
break;
case BTN_RIGHT:
mouse->r = event->value;
}
} }
else
{
mouse->x_rel += x;
if (video_driver_get_viewport_info(&vp))
{
mouse->x_abs += x;
if (mouse->x_abs < vp.x)
mouse->x_abs = vp.x;
else if (mouse->x_abs >= vp.x + vp.full_width)
mouse->x_abs = vp.x + vp.full_width - 1;
}
}
}
static int16_t udev_mouse_get_x(const udev_input_mouse_t *mouse)
{
video_viewport_t vp;
double src_width;
double x;
if (!video_driver_get_viewport_info(&vp))
return 0;
if (mouse->x_min < mouse->x_max) /* mouse coords are absolute */
src_width = mouse->x_max - mouse->x_min + 1;
else
src_width = vp.full_width;
x = (double)vp.width / src_width * mouse->x_rel;
return x + (x < 0 ? -0.5 : 0.5);
}
static void udev_mouse_set_y(udev_input_mouse_t *mouse, __s32 y, bool abs)
{
video_viewport_t vp;
if (abs)
{
mouse->y_rel += y - mouse->y_abs;
mouse->y_abs = y;
}
else
{
mouse->y_rel += y;
if (video_driver_get_viewport_info(&vp))
{
mouse->y_abs += y;
if (mouse->y_abs < vp.y)
mouse->y_abs = vp.y;
else if (mouse->y_abs >= vp.y + vp.full_height)
mouse->y_abs = vp.y + vp.full_height - 1;
}
}
}
static int16_t udev_mouse_get_y(const udev_input_mouse_t *mouse)
{
video_viewport_t vp;
double src_height;
double y;
if (!video_driver_get_viewport_info(&vp))
return 0;
if (mouse->y_min < mouse->y_max) /* mouse coords are absolute */
src_height = mouse->y_max - mouse->y_min + 1;
else
src_height = vp.full_height;
y = (double)vp.height / src_height * mouse->y_rel;
return y + (y < 0 ? -0.5 : 0.5);
}
static int16_t udev_mouse_get_pointer_x(const udev_input_mouse_t *mouse, bool screen)
{
video_viewport_t vp;
double src_min;
double src_width;
int32_t x;
if (!video_driver_get_viewport_info(&vp))
return 0;
if (mouse->x_min < mouse->x_max) /* mouse coords are absolute */
{
src_min = mouse->x_min;
src_width = mouse->x_max - mouse->x_min + 1;
}
else /* mouse coords are viewport relative */
{
src_min = vp.x;
if (screen)
src_width = vp.full_width;
else
src_width = vp.width;
}
x = -32767.0 + 65535.0 / src_width * (mouse->x_abs - src_min);
x += (x < 0 ? -0.5 : 0.5);
if (x < -0x7fff)
x = -0x7fff;
else if(x > 0x7fff)
x = 0x7fff;
return x;
}
static int16_t udev_mouse_get_pointer_y(const udev_input_mouse_t *mouse, bool screen)
{
video_viewport_t vp;
double src_min;
double src_height;
int32_t y;
if (!video_driver_get_viewport_info(&vp))
return 0;
if (mouse->y_min < mouse->y_max) /* mouse coords are absolute */
{
src_min = mouse->y_min;
src_height = mouse->y_max - mouse->y_min + 1;
}
else /* mouse coords are viewport relative */
{
src_min = vp.y;
if (screen)
src_height = vp.full_height;
else
src_height = vp.height;
}
y = -32767.0 + 65535.0 / src_height * (mouse->y_abs - src_min);
y += (y < 0 ? -0.5 : 0.5);
if (y < -0x7fff)
y = -0x7fff;
else if(y > 0x7fff)
y = 0x7fff;
return y;
} }
static void udev_handle_mouse(void *data, static void udev_handle_mouse(void *data,
const struct input_event *event, udev_input_device_t *dev) const struct input_event *event, udev_input_device_t *dev)
{ {
udev_input_mouse_t *mouse = &dev->state.mouse; udev_input_mouse_t *mouse = &dev->mouse;
switch (event->type) switch (event->type)
{ {
@ -303,10 +411,10 @@ static void udev_handle_mouse(void *data,
switch (event->code) switch (event->code)
{ {
case REL_X: case REL_X:
mouse->dlt_x += event->value; udev_mouse_set_x(mouse, event->value, false);
break; break;
case REL_Y: case REL_Y:
mouse->dlt_y += event->value; udev_mouse_set_y(mouse, event->value, false);
break; break;
case REL_WHEEL: case REL_WHEEL:
if (event->value == 1) if (event->value == 1)
@ -321,6 +429,19 @@ static void udev_handle_mouse(void *data,
mouse->whd = 1; mouse->whd = 1;
break; break;
} }
break;
case EV_ABS:
switch (event->code)
{
case ABS_X:
udev_mouse_set_x(mouse, event->value, true);
break;
case ABS_Y:
udev_mouse_set_y(mouse, event->value, true);
break;
}
break;
} }
} }
@ -330,6 +451,7 @@ static bool udev_input_add_device(udev_input_t *udev,
int fd; int fd;
struct stat st; struct stat st;
struct epoll_event event; struct epoll_event event;
struct input_absinfo absinfo;
udev_input_device_t **tmp; udev_input_device_t **tmp;
udev_input_device_t *device = NULL; udev_input_device_t *device = NULL;
@ -354,10 +476,38 @@ static bool udev_input_add_device(udev_input_t *udev,
strlcpy(device->devnode, devnode, sizeof(device->devnode)); strlcpy(device->devnode, devnode, sizeof(device->devnode));
/* Touchpads report in absolute coords. */ /* Touchpads report in absolute coords. */
if (type == UDEV_INPUT_TOUCHPAD && if (type == UDEV_INPUT_TOUCHPAD)
(ioctl(fd, EVIOCGABS(ABS_X), &device->state.touchpad.info_x) < 0 || {
ioctl(fd, EVIOCGABS(ABS_Y), &device->state.touchpad.info_y) < 0)) if (ioctl(fd, EVIOCGABS(ABS_X), &absinfo) < 0 ||
goto error; absinfo.minimum >= absinfo.maximum)
goto error;
device->mouse.x_min = absinfo.minimum;
device->mouse.x_max = absinfo.maximum;
if (ioctl(fd, EVIOCGABS(ABS_Y), &absinfo) < 0 ||
absinfo.minimum >= absinfo.maximum)
goto error;
device->mouse.y_min = absinfo.minimum;
device->mouse.y_max = absinfo.maximum;
}
/* UDEV_INPUT_MOUSE may report in absolute coords too */
else if (type == UDEV_INPUT_MOUSE && ioctl(fd, EVIOCGABS(ABS_X), &absinfo) >= 0)
{
if (absinfo.minimum >= absinfo.maximum)
goto error;
device->mouse.x_min = absinfo.minimum;
device->mouse.x_max = absinfo.maximum;
if (ioctl(fd, EVIOCGABS(ABS_Y), &absinfo) < 0 ||
absinfo.minimum >= absinfo.maximum)
goto error;
device->mouse.y_min = absinfo.minimum;
device->mouse.y_max = absinfo.maximum;
}
tmp = ( udev_input_device_t**)realloc(udev->devices, tmp = ( udev_input_device_t**)realloc(udev->devices,
(udev->num_devices + 1) * sizeof(*udev->devices)); (udev->num_devices + 1) * sizeof(*udev->devices));
@ -440,7 +590,7 @@ static void udev_input_handle_hotplug(udev_input_t *udev)
else if (val_touchpad && string_is_equal_fast(val_touchpad, "1", 1) && devnode) else if (val_touchpad && string_is_equal_fast(val_touchpad, "1", 1) && devnode)
{ {
dev_type = UDEV_INPUT_TOUCHPAD; dev_type = UDEV_INPUT_TOUCHPAD;
cb = udev_handle_touchpad; cb = udev_handle_mouse;
} }
else else
goto end; goto end;
@ -477,11 +627,11 @@ static void udev_input_get_pointer_position(int *x, int *y)
static bool udev_input_poll_hotplug_available(struct udev_monitor *dev) static bool udev_input_poll_hotplug_available(struct udev_monitor *dev)
{ {
struct pollfd fds; struct pollfd fds;
fds.fd = udev_monitor_get_fd(dev); fds.fd = udev_monitor_get_fd(dev);
fds.events = POLLIN; fds.events = POLLIN;
fds.revents = 0; fds.revents = 0;
return (poll(&fds, 1, 0) == 1) && (fds.revents & POLLIN); return (poll(&fds, 1, 0) == 1) && (fds.revents & POLLIN);
} }
@ -491,30 +641,22 @@ static void udev_input_poll(void *data)
int i, ret; int i, ret;
struct epoll_event events[32]; struct epoll_event events[32];
udev_input_mouse_t *mouse = NULL; udev_input_mouse_t *mouse = NULL;
int x = 0;
int y = 0;
udev_input_t *udev = (udev_input_t*)data; udev_input_t *udev = (udev_input_t*)data;
#ifdef HAVE_X11 #ifdef HAVE_X11
if (video_driver_display_type_get() == RARCH_DISPLAY_X11) if (video_driver_display_type_get() == RARCH_DISPLAY_X11)
udev_input_get_pointer_position(&x, &y); udev_input_get_pointer_position(&udev->pointer_x, &udev->pointer_y);
#endif #endif
for (i = 0; i < udev->num_devices; ++i) for (i = 0; i < udev->num_devices; ++i)
{ {
if (udev->devices[i]->type == UDEV_INPUT_MOUSE) if (udev->devices[i]->type == UDEV_INPUT_KEYBOARD)
{
mouse = &udev->devices[i]->state.mouse;
mouse->x = x;
mouse->y = y;
}
else if (udev->devices[i]->type == UDEV_INPUT_TOUCHPAD)
mouse = &udev->devices[i]->state.touchpad.mouse;
else
continue; continue;
mouse->dlt_x = 0; mouse = &udev->devices[i]->mouse;
mouse->dlt_y = 0;
mouse->x_rel = 0;
mouse->y_rel = 0;
mouse->wu = false; mouse->wu = false;
mouse->wd = false; mouse->wd = false;
mouse->whu = false; mouse->whu = false;
@ -548,18 +690,22 @@ static void udev_input_poll(void *data)
udev->joypad->poll(); udev->joypad->poll();
} }
static bool udev_pointer_is_off_window(udev_input_mouse_t *mouse) static bool udev_pointer_is_off_window(const udev_input_t *udev)
{ {
#ifdef HAVE_X11
struct video_viewport view; struct video_viewport view;
bool r = video_driver_get_viewport_info(&view); bool r = video_driver_get_viewport_info(&view);
if (r) if (r)
r = mouse->x < view.x || r = udev->pointer_x < view.x ||
mouse->x >= view.x + view.width || udev->pointer_x >= view.x + view.width ||
mouse->y < view.y || udev->pointer_y < view.y ||
mouse->y >= view.y + view.height; udev->pointer_y >= view.y + view.height;
return r; return r;
#else
return false;
#endif
} }
static int16_t udev_mouse_state(udev_input_t *udev, static int16_t udev_mouse_state(udev_input_t *udev,
@ -571,15 +717,15 @@ static int16_t udev_mouse_state(udev_input_t *udev,
return 0; return 0;
if (id != RETRO_DEVICE_ID_MOUSE_X && id != RETRO_DEVICE_ID_MOUSE_Y && if (id != RETRO_DEVICE_ID_MOUSE_X && id != RETRO_DEVICE_ID_MOUSE_Y &&
udev_pointer_is_off_window(mouse)) udev_pointer_is_off_window(udev))
return 0; return 0;
switch (id) switch (id)
{ {
case RETRO_DEVICE_ID_MOUSE_X: case RETRO_DEVICE_ID_MOUSE_X:
return screen ? mouse->x : mouse->dlt_x; return screen ? udev->pointer_x : udev_mouse_get_x(mouse);
case RETRO_DEVICE_ID_MOUSE_Y: case RETRO_DEVICE_ID_MOUSE_Y:
return screen ? mouse->y : mouse->dlt_y; return screen ? udev->pointer_y : udev_mouse_get_y(mouse);
case RETRO_DEVICE_ID_MOUSE_LEFT: case RETRO_DEVICE_ID_MOUSE_LEFT:
return mouse->l; return mouse->l;
case RETRO_DEVICE_ID_MOUSE_RIGHT: case RETRO_DEVICE_ID_MOUSE_RIGHT:
@ -610,9 +756,9 @@ static int16_t udev_lightgun_state(udev_input_t *udev,
switch (id) switch (id)
{ {
case RETRO_DEVICE_ID_LIGHTGUN_X: case RETRO_DEVICE_ID_LIGHTGUN_X:
return mouse->dlt_x; return udev_mouse_get_x(mouse);
case RETRO_DEVICE_ID_LIGHTGUN_Y: case RETRO_DEVICE_ID_LIGHTGUN_Y:
return mouse->dlt_y; return udev_mouse_get_y(mouse);
case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER:
return mouse->l; return mouse->l;
case RETRO_DEVICE_ID_LIGHTGUN_CURSOR: case RETRO_DEVICE_ID_LIGHTGUN_CURSOR:
@ -638,11 +784,11 @@ static int16_t udev_analog_pressed(const struct retro_keybind *binds,
input_conv_analog_id_to_bind_id(idx, id, &id_minus, &id_plus); input_conv_analog_id_to_bind_id(idx, id, &id_minus, &id_plus);
if ( binds[id_minus].valid if ( binds[id_minus].valid
&& BIT_GET(udev_key_state, && BIT_GET(udev_key_state,
rarch_keysym_lut[binds[id_minus].key])) rarch_keysym_lut[binds[id_minus].key]))
pressed_minus = -0x7fff; pressed_minus = -0x7fff;
if ( binds[id_plus].valid if ( binds[id_plus].valid
&& BIT_GET(udev_key_state, && BIT_GET(udev_key_state,
rarch_keysym_lut[binds[id_plus].key])) rarch_keysym_lut[binds[id_plus].key]))
pressed_plus = 0x7fff; pressed_plus = 0x7fff;
@ -653,45 +799,17 @@ static int16_t udev_analog_pressed(const struct retro_keybind *binds,
static int16_t udev_pointer_state(udev_input_t *udev, static int16_t udev_pointer_state(udev_input_t *udev,
unsigned port, unsigned id, bool screen) unsigned port, unsigned id, bool screen)
{ {
struct video_viewport vp; udev_input_mouse_t *mouse = udev_get_mouse(udev, port);
bool inside = false;
int16_t res_x = 0;
int16_t res_y = 0;
int16_t res_screen_x = 0;
int16_t res_screen_y = 0;
udev_input_mouse_t *mouse = udev_get_mouse(udev, port);
vp.x = 0;
vp.y = 0;
vp.width = 0;
vp.height = 0;
vp.full_width = 0;
vp.full_height = 0;
if (!mouse) if (!mouse)
return 0; return 0;
if (!(video_driver_translate_coord_viewport_wrap(&vp,
mouse->x, mouse->y, &res_x, &res_y, &res_screen_x, &res_screen_y)))
return 0;
if (screen)
{
res_x = res_screen_x;
res_y = res_screen_y;
}
inside = (res_x >= -0x7fff) && (res_y >= -0x7fff);
if (!inside)
return 0;
switch (id) switch (id)
{ {
case RETRO_DEVICE_ID_POINTER_X: case RETRO_DEVICE_ID_POINTER_X:
return res_x; return udev_mouse_get_pointer_x(mouse, screen);
case RETRO_DEVICE_ID_POINTER_Y: case RETRO_DEVICE_ID_POINTER_Y:
return res_y; return udev_mouse_get_pointer_y(mouse, screen);
case RETRO_DEVICE_ID_POINTER_PRESSED: case RETRO_DEVICE_ID_POINTER_PRESSED:
return mouse->l; return mouse->l;
} }
@ -878,13 +996,13 @@ static void *udev_input_init(const char *joypad_driver)
goto error; goto error;
} }
if (!open_devices(udev, UDEV_INPUT_TOUCHPAD, udev_handle_touchpad)) if (!open_devices(udev, UDEV_INPUT_TOUCHPAD, udev_handle_mouse))
{ {
RARCH_ERR("Failed to open touchpads.\n"); RARCH_ERR("Failed to open touchpads.\n");
goto error; goto error;
} }
/* If using KMS and we forgot this, /* If using KMS and we forgot this,
* we could lock ourselves out completely. */ * we could lock ourselves out completely. */
if (!udev->num_devices) if (!udev->num_devices)
RARCH_WARN("[udev]: Couldn't open any keyboard, mouse or touchpad. Are permissions set correctly for /dev/input/event*?\n"); RARCH_WARN("[udev]: Couldn't open any keyboard, mouse or touchpad. Are permissions set correctly for /dev/input/event*?\n");