* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* Linux console mouse driver for event interface (EVDEV).
*
* By Annie Testes.
*
* Handles all inputs that fill evdev with informations
* about movement and click.
*
* Hacked for new mouse API by Peter Wang.
*
* See readme.txt for copyright information.
*/
#define ALLEGRO_NO_COMPATIBILITY
#include "allegro5/allegro.h"
#ifdef ALLEGRO_HAVE_LINUX_INPUT_H
ALLEGRO_DEBUG_CHANNEL("lmseev");
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_mouse.h"
#include "allegro5/platform/aintunix.h"
#include "allegro5/platform/aintlnx.h"
#ifdef ALLEGRO_RASPBERRYPI
#include "allegro5/internal/aintern_system.h"
#include "allegro5/internal/aintern_display.h"
#include "allegro5/internal/aintern_vector.h"
#endif
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#define PREFIX_I "al-evdev INFO: "
#define PREFIX_W "al-evdev WARNING: "
#define PREFIX_E "al-evdev ERROR: "
typedef struct AL_MOUSE_EVDEV
{
ALLEGRO_MOUSE parent;
int fd;
ALLEGRO_MOUSE_STATE state;
} AL_MOUSE_EVDEV;
static AL_MOUSE_EVDEV the_mouse;
static void process_new_data(void *unused);
static void process_event(struct input_event *event);
static void handle_button_event(unsigned int button, bool is_down);
static void handle_axis_event(int dx, int dy, int dz);
static void generate_mouse_event(unsigned int type,
int x, int y, int z,
int dx, int dy, int dz,
unsigned int button);
bool _al_evdev_set_mouse_range(int x1, int y1, int x2, int y2);
* Tools data
*/
* This mode is the behavior the user sees, it is not the way the device
* reports the position.
* Tools that send relative informations can only be in relative mode,
* tools that send absolute informations can be either in absolute mode or in
* relative mode */
typedef enum {
MODE_RELATIVE,
MODE_ABSOLUTE
} MODE;
typedef struct TOOL {
int tool_id;
MODE mode;
} TOOL;
static TOOL tools[] = {
{ BTN_TOOL_MOUSE, MODE_RELATIVE },
{ BTN_TOOL_PEN, MODE_ABSOLUTE },
{ BTN_TOOL_RUBBER, MODE_ABSOLUTE },
{ BTN_TOOL_BRUSH, MODE_ABSOLUTE },
{ BTN_TOOL_PENCIL, MODE_ABSOLUTE },
{ BTN_TOOL_AIRBRUSH, MODE_ABSOLUTE },
{ BTN_TOOL_FINGER, MODE_ABSOLUTE },
{ BTN_TOOL_LENS, MODE_ABSOLUTE },
{ -1, MODE_ABSOLUTE }
};
static TOOL *default_tool = tools + 0;
static TOOL *no_tool = tools + (sizeof(tools)/sizeof(tools[0]) - 1);
static TOOL *current_tool;
* Returns the tool with id 'tool_id' which is one of the BTN_TOOL_... defined
* in linux/input.h. Returns 'default_tool' if not found.
*/
static TOOL *find_tool(int tool_id)
{
TOOL *t;
for (t=tools; t!=no_tool; t++) {
if (t->tool_id == tool_id)
return t;
}
return default_tool;
}
* Pointer axis
*/
typedef struct AXIS {
int in_min;
int in_max;
int out_min;
int out_max;
float speed;
int mickeys;
float scale;
than common mice */
int in_abs;
whether the output is absolute or relative */
int out_abs;
} AXIS;
#define IN_RANGE(axis) ( (axis).in_max-(axis).in_min+1 )
#define OUT_RANGE(axis) ( (axis).out_max-(axis).out_min+1 )
* maps an input absolute position to a screen position
*/
static int in_to_screen(const AXIS *axis, int v)
{
return (((v-axis->in_min) * OUT_RANGE(*axis)) / IN_RANGE(*axis)) + axis->out_min;
}
* returns the new screen position, given the input relative one.
* The tool mode is always relative
*/
static int rel_event(AXIS *axis, int v)
{
int ret = axis->out_abs + v*axis->speed;
axis->mickeys += v;
axis->in_abs += v;
return ret;
}
* returns the new screen position, given the input absolute one,
* and depending on the tool mode
*/
static int abs_event(AXIS *axis, MODE mode, int v)
{
if (mode == MODE_ABSOLUTE) {
axis->mickeys = 0;
axis->in_abs = v;
return in_to_screen(axis, v);
}
else {
int value = (v-axis->in_abs)*axis->scale;
axis->mickeys += value;
axis->in_abs = v;
return axis->out_abs + value*axis->speed;
}
}
* returns the input absolute position
*/
static void get_axis_value(int fd, AXIS *axis, int type)
{
int abs[5];
int ret = ioctl(fd, EVIOCGABS(type), abs);
if (ret>=0) {
axis->in_abs = abs[0];
}
}
* returns true if the device generates the event
*/
static int has_event(int fd, unsigned short type, unsigned short code)
{
const unsigned int len = sizeof(unsigned long)*8;
const unsigned int max = _ALLEGRO_MAX(EV_MAX, _ALLEGRO_MAX(KEY_MAX, _ALLEGRO_MAX(REL_MAX, _ALLEGRO_MAX(ABS_MAX, _ALLEGRO_MAX(LED_MAX, _ALLEGRO_MAX(SND_MAX, FF_MAX))))));
unsigned long bits[(max+len-1)/len];
if (ioctl(fd, EVIOCGBIT(type, max), bits)) {
return (bits[code/len] >> (code%len)) & 1;
}
return 0;
}
* return the number of buttons
*/
static int get_num_buttons(int fd)
{
if (has_event(fd, EV_KEY, BTN_MIDDLE))
return 3;
if (has_event(fd, EV_KEY, BTN_RIGHT))
return 2;
if (has_event(fd, EV_KEY, BTN_MOUSE))
return 1;
return 0;
}
static AXIS x_axis;
static AXIS y_axis;
static AXIS z_axis;
* Initialization functions
*/
* initialize an AXIS structure, from the device and the config file
*/
static void init_axis(int fd, AXIS *axis, const char *name, const char *section, int type)
{
#if 0
char tmp1[256];
char tmp2[256];
char tmp3[256];
#endif
int abs[5];
int config_speed;
(void)name;
(void)section;
config_speed = 1;
axis->scale = 1;
if (ioctl(fd, EVIOCGABS(type), abs)>=0) {
if (axis->in_min==0)
axis->in_min=abs[1];
if (axis->in_max==0)
axis->in_max=abs[2];
axis->in_abs = abs[0];
axis->scale = 320.0*config_speed/IN_RANGE(*axis);
}
if (axis->in_min>axis->in_max) {
axis->in_min = axis->in_max = 0;
axis->scale = 1;
}
axis->out_min = INT_MIN;
axis->out_max = INT_MAX;
axis->speed = 1;
axis->mickeys = 0;
}
* initialize the tools and axis
*/
static void init_tablet(int fd)
{
int default_abs = default_tool->mode==MODE_ABSOLUTE;
if (default_abs) {
default_tool->mode = MODE_ABSOLUTE;
}
else {
default_tool->mode = MODE_RELATIVE;
}
init_axis(fd, &x_axis, "x", "mouse", ABS_X);
init_axis(fd, &y_axis, "y", "mouse", ABS_Y);
init_axis(fd, &z_axis, "z", "mouse", ABS_Z);
}
* Process input functions
*/
* process events of type key (button clicks and vicinity events are currently
* supported)
*/
static void process_key(const struct input_event *event)
{
switch (event->code) {
* if event->value is 1 the button has just been pressed
* if event->value is 0 the button has just been released */
case BTN_LEFT:
case BTN_TOUCH:
handle_button_event(1, event->value);
break;
case BTN_RIGHT:
case BTN_STYLUS:
handle_button_event(2, event->value);
break;
case BTN_MIDDLE:
case BTN_STYLUS2:
handle_button_event(3, event->value);
break;
* if event->value is 1, the tool enters the tablet vicinity
* if event->value is 0, the tool leaves the tablet vicinity */
case BTN_TOOL_MOUSE:
case BTN_TOOL_PEN:
case BTN_TOOL_RUBBER:
case BTN_TOOL_BRUSH:
case BTN_TOOL_PENCIL:
case BTN_TOOL_AIRBRUSH:
case BTN_TOOL_FINGER:
case BTN_TOOL_LENS:
if (event->value) {
current_tool = find_tool(event->code);
get_axis_value(the_mouse.fd, &x_axis, ABS_X);
get_axis_value(the_mouse.fd, &y_axis, ABS_Y);
get_axis_value(the_mouse.fd, &z_axis, ABS_Z);
#ifdef ABS_WHEEL
get_axis_value(the_mouse.fd, &z_axis, ABS_WHEEL);
#endif
}
else {
current_tool = no_tool;
}
break;
}
}
static void handle_button_event(unsigned int button, bool is_down)
{
unsigned int event_type;
if (is_down) {
the_mouse.state.buttons |= (1 << (button-1));
event_type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
}
else {
the_mouse.state.buttons &=~ (1 << (button-1));
event_type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
}
generate_mouse_event(
event_type,
the_mouse.state.x, the_mouse.state.y, the_mouse.state.z,
0, 0, 0,
button);
}
* process relative events (x, y and z movement are currently supported)
*/
static void process_rel(const struct input_event *event)
{
if (current_tool!=no_tool) {
switch (event->code) {
case REL_X:
x_axis.out_abs = rel_event(&x_axis, event->value);
handle_axis_event(x_axis.mickeys, 0, 0);
x_axis.mickeys = 0;
break;
case REL_Y:
y_axis.out_abs = rel_event(&y_axis, event->value);
handle_axis_event(0, y_axis.mickeys, 0);
y_axis.mickeys = 0;
break;
#ifdef REL_WHEEL
case REL_WHEEL:
#endif
case REL_Z:
z_axis.out_abs = rel_event(&z_axis, event->value);
handle_axis_event(0, 0, z_axis.mickeys);
z_axis.mickeys = 0;
break;
}
}
}
* process absolute events (x, y and z movement are currently supported)
* TODO: missing handle_axis_event calls
*/
static void process_abs(const struct input_event *event)
{
if (current_tool!=no_tool) {
switch (event->code) {
case ABS_X:
x_axis.out_abs = abs_event(&x_axis, current_tool->mode, event->value);
break;
case ABS_Y:
y_axis.out_abs = abs_event(&y_axis, current_tool->mode, event->value);
break;
#ifdef ABS_WHEEL
case ABS_WHEEL:
#endif
case ABS_Z:
z_axis.out_abs = abs_event(&z_axis, current_tool->mode, event->value);
break;
}
}
}
static void handle_axis_event(int dx, int dy, int dz)
{
if (current_tool != no_tool) {
x_axis.out_abs = _ALLEGRO_CLAMP(x_axis.out_min, x_axis.out_abs, x_axis.out_max);
y_axis.out_abs = _ALLEGRO_CLAMP(y_axis.out_min, y_axis.out_abs, y_axis.out_max);
the_mouse.state.x = x_axis.out_abs;
the_mouse.state.y = y_axis.out_abs;
the_mouse.state.z = z_axis.out_abs * al_get_mouse_wheel_precision();
dz *= al_get_mouse_wheel_precision();
generate_mouse_event(
ALLEGRO_EVENT_MOUSE_AXES,
the_mouse.state.x, the_mouse.state.y, the_mouse.state.z,
dx, dy, dz,
0);
}
}
* Open the specified device file and check that it is a mouse device.
* Returns the file descriptor if successful or a negative number on error.
*/
static int open_mouse_device (const char *device_file)
{
int fd;
fd = open (device_file, O_RDONLY | O_NONBLOCK);
if (fd >= 0) {
ALLEGRO_DEBUG("Opened device %s\n", device_file);
if (has_event(fd, EV_KEY, BTN_MOUSE)) {
ALLEGRO_DEBUG("Device %s was a mouse.\n", device_file);
}
else {
ALLEGRO_DEBUG("Device %s was not mouse, closing.\n", device_file);
close(fd);
fd = -1;
}
}
return fd;
}
* Here we open the mouse device, initialise anything that needs it,
* and chain to the framework init routine.
*/
static bool mouse_init (void)
{
char tmp1[128], tmp2[128];
const char *udevice;
*/
current_tool = default_tool;
* devices. */
const char *device_name[] = { "/dev/input/event0",
"/dev/input/event1",
"/dev/input/event2",
"/dev/input/event3",
NULL };
int i;
ALLEGRO_DEBUG("Trying /dev/input/event[0-3] devices\n");
for (i=0; device_name[i]; i++) {
the_mouse.fd = open_mouse_device (device_name[i]);
if (the_mouse.fd >= 0)
break;
}
if (!device_name[i]) {
return false;
}
init_tablet(the_mouse.fd);
_al_event_source_init(&the_mouse.parent.es);
_al_unix_start_watching_fd(the_mouse.fd, process_new_data, &the_mouse);
return true;
}
* Chain to the framework, then uninitialise things.
*/
static void mouse_exit (void)
{
_al_unix_stop_watching_fd(the_mouse.fd);
_al_event_source_free(&the_mouse.parent.es);
close(the_mouse.fd);
* we return to the user is always the same.
*/
memset(&the_mouse, 0, sizeof the_mouse);
the_mouse.fd = -1;
}
* Returns the address of a ALLEGRO_MOUSE structure representing the mouse.
*/
static ALLEGRO_MOUSE *mouse_get_mouse(void)
{
return (ALLEGRO_MOUSE *)&the_mouse;
}
* Return the number of buttons on the mouse.
*/
static unsigned int mouse_get_mouse_num_buttons(void)
{
ASSERT(the_mouse.fd >= 0);
return get_num_buttons(the_mouse.fd);
}
* Return the number of axes on the mouse.
*/
static unsigned int mouse_get_mouse_num_axes(void)
{
ASSERT(the_mouse.fd >= 0);
return 3;
}
*
*/
static bool mouse_set_mouse_xy(ALLEGRO_DISPLAY *display, int x, int y)
{
(void)display;
_al_event_source_lock(&the_mouse.parent.es);
{
int dx, dy;
x_axis.out_abs = _ALLEGRO_CLAMP(x_axis.out_min, x, x_axis.out_max);
y_axis.out_abs = _ALLEGRO_CLAMP(y_axis.out_min, y, y_axis.out_max);
x_axis.mickeys = 0;
y_axis.mickeys = 0;
dx = x_axis.out_abs - the_mouse.state.x;
dy = y_axis.out_abs - the_mouse.state.y;
if ((dx != 0) && (dy != 0)) {
the_mouse.state.x = x_axis.out_abs;
the_mouse.state.y = y_axis.out_abs;
generate_mouse_event(
ALLEGRO_EVENT_MOUSE_AXES,
the_mouse.state.x, the_mouse.state.y, the_mouse.state.z,
dx, dy, 0,
0);
}
}
_al_event_source_unlock(&the_mouse.parent.es);
return true;
}
*
* Number of mickeys to cross the screen horizontally: speed * 320.
*/
static bool mouse_set_mouse_axis(int which, int z)
{
if (which != 2) {
return false;
}
_al_event_source_lock(&the_mouse.parent.es);
{
int dz;
z_axis.out_abs = z;
dz = z_axis.out_abs - the_mouse.state.z;
if (dz != 0) {
the_mouse.state.z = z_axis.out_abs;
generate_mouse_event(
ALLEGRO_EVENT_MOUSE_AXES,
the_mouse.state.x, the_mouse.state.y, the_mouse.state.z,
0, 0, dz,
0);
}
}
_al_event_source_unlock(&the_mouse.parent.es);
return true;
}
*
*/
bool _al_evdev_set_mouse_range(int x1, int y1, int x2, int y2)
{
_al_event_source_lock(&the_mouse.parent.es);
{
int dx, dy;
x_axis.out_min = x1;
y_axis.out_min = y1;
x_axis.out_max = x2;
y_axis.out_max = y2;
x_axis.out_abs = _ALLEGRO_CLAMP(x_axis.out_min, x_axis.out_abs, x_axis.out_max);
y_axis.out_abs = _ALLEGRO_CLAMP(y_axis.out_min, y_axis.out_abs, y_axis.out_max);
dx = x_axis.out_abs - the_mouse.state.x;
dy = y_axis.out_abs - the_mouse.state.y;
if ((dx != 0) && (dy != 0)) {
the_mouse.state.x = x_axis.out_abs;
the_mouse.state.y = y_axis.out_abs;
generate_mouse_event(
ALLEGRO_EVENT_MOUSE_AXES,
the_mouse.state.x, the_mouse.state.y, the_mouse.state.z,
dx, dy, 0,
0);
}
}
_al_event_source_unlock(&the_mouse.parent.es);
return true;
}
* Copy the current mouse state into RET_STATE, with any necessary locking.
*/
static void mouse_get_state(ALLEGRO_MOUSE_STATE *ret_state)
{
_al_event_source_lock(&the_mouse.parent.es);
{
*ret_state = the_mouse.state;
}
_al_event_source_unlock(&the_mouse.parent.es);
}
* Process new data arriving in the keyboard's fd.
*/
static void process_new_data(void *data)
{
ASSERT((AL_MOUSE_EVDEV *)data == &the_mouse);
_al_event_source_lock(&the_mouse.parent.es);
{
struct input_event events[32];
int bytes, nr, i;
while ((bytes = read(the_mouse.fd, &events, sizeof events)) > 0) {
nr = bytes / sizeof(events[0]);
for (i = 0; i < nr; i++) {
process_event(&events[i]);
}
}
}
_al_event_source_unlock(&the_mouse.parent.es);
}
static void process_event(struct input_event *event)
{
switch (event->type) {
case EV_KEY:
process_key(event);
break;
case EV_REL:
process_rel(event);
break;
case EV_ABS:
process_abs(event);
break;
}
}
static void generate_mouse_event(unsigned int type,
int x, int y, int z,
int dx, int dy, int dz,
unsigned int button)
{
ALLEGRO_EVENT event;
if (!_al_event_source_needs_to_generate_event(&the_mouse.parent.es))
return;
event.mouse.type = type;
event.mouse.timestamp = al_get_time();
event.mouse.display = NULL;
event.mouse.x = x;
event.mouse.y = y;
event.mouse.z = z;
event.mouse.dx = dx;
event.mouse.dy = dy;
event.mouse.dz = dz;
event.mouse.button = button;
event.mouse.pressure = 0.0;
_al_event_source_emit_event(&the_mouse.parent.es, &event);
}
ALLEGRO_MOUSE_DRIVER _al_mousedrv_linux_evdev =
{
AL_MOUSEDRV_LINUX_EVDEV,
"",
"",
"Linux EVDEV mouse (and tablet)",
mouse_init,
mouse_exit,
mouse_get_mouse,
mouse_get_mouse_num_buttons,
mouse_get_mouse_num_axes,
mouse_set_mouse_xy,
mouse_set_mouse_axis,
mouse_get_state
};
#endif
* Local Variables:
* c-basic-offset: 3
* indent-tabs-mode: nil
* End:
*/