usb: support multi touch devices

Issue #1444
This commit is contained in:
Alexander Boettcher 2015-02-25 17:46:26 +01:00 committed by Christian Helmuth
parent 71c3fa53da
commit cd764a6aa6
6 changed files with 156 additions and 51 deletions

View File

@ -45,10 +45,15 @@ hid xml tag:
!...
!<hid>
! <screen width="1024" height="768"/>
! <touchscreen width="1024" height="768" multitouch="no"/>
!<hid/>
!...
If a touchscreen is multi-touch-capable than the multitouch attribute gears
which type of Genode input events are generated. If set to 'no' (default)
than absolute events are generated and no multitouch events. If set to 'yes'
solely multitouch events are generated.
Storage
~~~~~~~

View File

@ -3781,7 +3781,8 @@ static inline void dump_stack(void) { }
enum input_event_type {
EVENT_TYPE_PRESS, EVENT_TYPE_RELEASE, /* key press and release */
EVENT_TYPE_MOTION, /* any type of (pointer) motion */
EVENT_TYPE_WHEEL /* mouse scroll wheel */
EVENT_TYPE_WHEEL, /* mouse scroll wheel */
EVENT_TYPE_TOUCH /* touchscreen events */
};
struct input_handle;
@ -3822,13 +3823,13 @@ typedef void (*genode_input_event_cb)(enum input_event_type type,
* \return 0 on success; !0 otherwise
*/
void genode_input_register(genode_input_event_cb handler, unsigned long res_x,
unsigned long res_y);
unsigned long res_y, bool multitouch);
void genode_evdev_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value);
void start_input_service(void *ep, unsigned long res_x, unsigned long res_y);
void start_input_service(void *ep, void *);
/******************

View File

@ -37,7 +37,8 @@ struct Services
* Screen resolution used by touch devices to convert touchscreen
* absolute coordinates to screen absolute coordinates
*/
unsigned long screen_width = 0;
bool multitouch = false;
unsigned long screen_width = 0;
unsigned long screen_height = 0;
Services()
@ -49,9 +50,10 @@ struct Services
hid = true;
try {
Genode::Xml_node node_screen = node_hid.sub_node("screen");
Genode::Xml_node node_screen = node_hid.sub_node("touchscreen");
node_screen.attribute("width").value(&screen_width);
node_screen.attribute("height").value(&screen_height);
multitouch = node_screen.attribute("multitouch").has_value("yes");
} catch (...) {
screen_width = screen_height = 0;
PDBG("Could not read screen resolution in config node");

View File

@ -4,6 +4,7 @@
* \author Dirk Vogt <dvogt@os.inf.tu-dresden.de>
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \author Christian Menard <christian.menard@ksyslabs.org>
* \author Alexander Boettcher <alexander.boettcher@genode-labs.com>
* \date 2009-04-20
*
* The original implementation was in the L4Env from the TUD:OS group
@ -12,7 +13,7 @@
*/
/*
* Copyright (C) 2009-2014 Genode Labs GmbH
* Copyright (C) 2009-2015 Genode Labs GmbH
* Copyright (C) 2014 Ksys Labs LLC
*
* This file is part of the Genode OS framework, which is distributed
@ -28,9 +29,41 @@
/* Callback function to Genode subsystem */
static genode_input_event_cb handler;;
static unsigned long screen_x = 0;
static unsigned long screen_y = 0;
static unsigned long screen_x = 0;
static unsigned long screen_y = 0;
static bool disable_multitouch = true;
static bool disable_absolute = false;
static struct slot
{
int id; /* current tracking id */
int x; /* last reported x axis */
int y; /* last reported y axis */
int event; /* last reported ABS_MT_ event */
} slots[16];
static bool transform(struct input_handle *handle, int x, int y,
int * arg_ax, int * arg_ay)
{
int const min_x_dev = input_abs_get_min(handle->dev, ABS_X);
int const min_y_dev = input_abs_get_min(handle->dev, ABS_Y);
int const max_x_dev = input_abs_get_max(handle->dev, ABS_X);
int const max_y_dev = input_abs_get_max(handle->dev, ABS_Y);
int const max_y_norm = max_y_dev - min_y_dev;
int const max_x_norm = max_x_dev - min_x_dev;
if (!max_x_norm || !max_y_norm || !arg_ax || !arg_ay ||
x < min_x_dev || y < min_y_dev || x > max_x_dev || y > max_y_dev) {
printk("Ignore input source with coordinates out of range\n");
return false;
}
*arg_ax = screen_x * (x - min_x_dev) / (max_x_norm);
*arg_ay = screen_y * (y - min_y_dev) / (max_y_norm);
return true;
}
void genode_evdev_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
@ -39,8 +72,8 @@ void genode_evdev_event(struct input_handle *handle, unsigned int type,
static unsigned long count = 0;
#endif
static int last_ax = -1; /* store the last absolute x value */
static int slot = 0; /* store current input slot */
/* filter sound events */
if (test_bit(EV_SND, handle->dev->evbit)) return;
@ -60,8 +93,12 @@ void genode_evdev_event(struct input_handle *handle, unsigned int type,
case EV_KEY:
arg_keycode = code;
/* map BTN_TOUCH events to BTN_LEFT */
if (code == BTN_TOUCH)
/* don't generate press/release events for multitouch devices */
if (code == BTN_TOUCH && !disable_multitouch)
return;
/* map BTN_TOUCH events to BTN_LEFT for absolute events */
if (code == BTN_TOUCH && !disable_absolute)
arg_keycode = BTN_LEFT;
switch (value) {
@ -83,68 +120,117 @@ void genode_evdev_event(struct input_handle *handle, unsigned int type,
case EV_ABS:
switch (code) {
case ABS_WHEEL:
if (disable_absolute) return;
arg_type = EVENT_TYPE_WHEEL;
arg_ry = value;
break;
case ABS_X:
case ABS_MT_POSITION_X:
if (code == ABS_X && disable_absolute) return;
if (code == ABS_MT_POSITION_X && disable_multitouch) return;
if (((slots[slot].event == ABS_MT_POSITION_X) ||
(slots[slot].event == ABS_X)) && (slots[slot].y != -1)) {
arg_keycode = slot;
arg_type = code == ABS_X ? EVENT_TYPE_MOTION : EVENT_TYPE_TOUCH;
arg_ax = slots[slot].x;
arg_ay = slots[slot].y;
if (screen_x && screen_y &&
!transform(handle, arg_ax, arg_ay, &arg_ax, &arg_ay))
return;
if (handler)
handler(arg_type, arg_keycode, arg_ax, arg_ay, arg_rx, arg_ry);
}
slots[slot].event = code;
/*
* Don't create an input event yet. Store the value and wait for the
* subsequent Y event.
*/
last_ax = value;
slots[slot].x = value;
return;
case ABS_Y:
case ABS_MT_POSITION_Y:
if (code == ABS_Y && disable_absolute) return;
if (code == ABS_MT_POSITION_Y && disable_multitouch) return;
slots[slot].event = code;
slots[slot].y = value;
if (slots[slot].x == -1)
return;
/*
* Create a unified input event with absolute positions on x and y
* axis.
*/
arg_type = EVENT_TYPE_MOTION;
arg_ax = last_ax;
arg_ay = value;
arg_keycode = slot;
arg_type = code == ABS_Y ? EVENT_TYPE_MOTION : EVENT_TYPE_TOUCH;
arg_ax = slots[slot].x;
arg_ay = value;
/* transform if requested */
if (screen_x && screen_y) {
int const min_x_dev = input_abs_get_min(handle->dev, ABS_X);
int const min_y_dev = input_abs_get_min(handle->dev, ABS_Y);
int const max_x_dev = input_abs_get_max(handle->dev, ABS_X);
int const max_y_dev = input_abs_get_max(handle->dev, ABS_Y);
int const max_y_norm = max_y_dev - min_y_dev;
int const max_x_norm = max_x_dev - min_x_dev;
if (screen_x && screen_y &&
!transform(handle, arg_ax, value, &arg_ax, &arg_ay))
return;
if ((max_x_norm == 0) || (max_y_norm == 0) ||
(last_ax < min_x_dev) || (value < min_y_dev) ||
(last_ax > max_x_dev) || (value > max_y_dev))
{
printk("Ignore input source with coordinates out of range\n");
return;
}
break;
arg_ax = screen_x * (last_ax - min_x_dev) / (max_x_norm);
arg_ay = screen_y * (value - min_y_dev) / (max_y_norm);
}
case ABS_MT_TRACKING_ID:
last_ax = -1;
if (arg_ax == -1) {
printk("Ignore absolute Y event without a preceeding X event\n");
if (disable_multitouch) return;
if (value != -1) {
if (slots[slot].id != -1)
dde_kit_printf("warning:: old tracking id in use and got new one\n");
slots[slot].id = value;
return;
}
/* send end of slot usage event for clients */
arg_keycode = slot;
arg_type = EVENT_TYPE_TOUCH;
arg_ax = slots[slot].x < 0 ? 0 : slots[slot].x;
arg_ay = slots[slot].y < 0 ? 0 : slots[slot].y;
arg_rx = arg_ry = -1;
if (screen_x && screen_y)
transform(handle, arg_ax, arg_ay, &arg_ax, &arg_ay);
slots[slot].event = slots[slot].x = slots[slot].y = -1;
slots[slot].id = value;
break;
case ABS_WHEEL:
case ABS_MT_SLOT:
/*
* XXX I do not know, how to handle this correctly. At least, this
* scheme works on Qemu.
*/
arg_type = EVENT_TYPE_WHEEL;
arg_ry = value;
break;
if (disable_multitouch) return;
if (value >= sizeof(slots) / sizeof(slots[0])) {
dde_kit_printf("warning: drop slot id %d\n", value);
return;
}
slot = value;
return;
default:
printk("Unknown absolute event code %d - not handled\n", code);
return;
}
break;
@ -194,10 +280,18 @@ void genode_evdev_event(struct input_handle *handle, unsigned int type,
void genode_input_register(genode_input_event_cb h, unsigned long res_x,
unsigned long res_y)
unsigned long res_y, bool multitouch)
{
unsigned i = 0;
for (i = 0; i < sizeof(slots) / sizeof(slots[0]); i++)
slots[i].id = slots[i].event = slots[i].x = slots[i].y = -1;
handler = h;
/* XXX make it per usb device configurable XXX */
screen_x = res_x;
screen_y = res_y;
disable_multitouch = !multitouch;
disable_absolute = !disable_multitouch;
}

View File

@ -22,6 +22,7 @@
#include <lx_emul.h>
#include <extern_c_end.h>
#include "platform.h"
#undef RELEASE
using namespace Genode;
@ -65,6 +66,7 @@ static void input_callback(enum input_event_type type,
case EVENT_TYPE_RELEASE: t = Input::Event::RELEASE; break;
case EVENT_TYPE_MOTION: t = Input::Event::MOTION; break;
case EVENT_TYPE_WHEEL: t = Input::Event::WHEEL; break;
case EVENT_TYPE_TOUCH: t = Input::Event::TOUCH; break;
}
input_session().submit(Input::Event(t, code,
@ -73,11 +75,13 @@ static void input_callback(enum input_event_type type,
}
void start_input_service(void *ep_ptr, unsigned long res_x, unsigned long res_y)
void start_input_service(void *ep_ptr, void * service_ptr)
{
Rpc_entrypoint *ep = static_cast<Rpc_entrypoint *>(ep_ptr);
Services *service = static_cast<Services *>(service_ptr);
env()->parent()->announce(ep->manage(&input_root(ep)));
genode_input_register(input_callback, res_x, res_y);
genode_input_register(input_callback, service->screen_width,
service->screen_height, service->multitouch);
}

View File

@ -44,7 +44,7 @@ extern "C" void module_ch_driver_init();
extern "C" void module_mt_driver_init();
extern "C" void module_raw_driver_init();
extern "C" void start_input_service(void *ep, unsigned long, unsigned long);
extern "C" void start_input_service(void *ep, void *services);
Routine *Routine::_current = 0;
Routine *Routine::_dead = 0;
@ -94,8 +94,7 @@ void start_usb_driver(Server::Entrypoint &ep)
Services services;
if (services.hid)
start_input_service(&ep.rpc_ep(), services.screen_width,
services.screen_height);
start_input_service(&ep.rpc_ep(), &services);
Timer::init(ep);
Irq::init(ep);