/* * EvRA - Event Routing Application * * Copyright (C) 2004 Simon Budig * * * This program 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 Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Version 0.epsilon ("proof of concept") * * Please note that this application is only useful for you if you * own a Griffin Technology Powermate. Eventuelly EvRA might evolve * into a more versatile utility. * * The movement of the Powermate gets mapped to "cursor left/cursor * right" key events. It depends on the currently focused application * how they get interpreted. (In an image view of the GIMP this changes * the opacity of the current paint tool). * * compile with * gcc -Wall `pkg-config --cflags --libs libwnck-1.0` \ * `pkg-config --cflags --libs gtk+-2.0` \ * -L/usr/X11R6/lib -lX11 -lXtst -o evra evra.c * * You need devel-headers for libwnck and gtk+ (GNOME) as well as * the X11 header files, as well as an X-Server that includes the * XTest-Extension. * */ #include #include #include #include #include #include #include #include #include #include #include #include #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include #define NUM_EVENT_DEVICES 16 typedef struct _EvraData EvraData; struct _EvraData { WnckScreen *screen; gint input_fd; guint event_source_id; GIOChannel *ioc; GMainLoop *mainloop; Display *display; gdouble gimp_opacity; }; typedef enum _EvraEvent EvraEvent; enum _EvraEvent { EVRA_TURN_LEFT, EVRA_TURN_RIGHT, EVRA_BUTTON_PRESS, EVRA_BUTTON_RELEASE, EVRA_TURN_LEFT_PRESSED, EVRA_TURN_RIGHT_PRESSED, }; EvraData *evra; void evra_print_window (EvraData *evra, gpointer data) { WnckWindow *win; WnckApplication *app; XClassHint *class_hint; Window xwin; win = wnck_screen_get_active_window (evra->screen); if (win) { xwin = wnck_window_get_xid (win); app = wnck_window_get_application (win); class_hint = XAllocClassHint (); XGetClassHint (evra->display, xwin, class_hint); g_printerr ("\"%s\" /%s/ (%lx). -- |%s|%s|\n", wnck_window_get_name (win), wnck_application_get_name (app), wnck_window_get_xid (win), class_hint->res_name, class_hint->res_class); XFree (class_hint->res_name); XFree (class_hint->res_class); XFree (class_hint); } else { g_printerr ("\n"); } } gboolean evra_test_application (EvraData *evra, gchar *appident) { WnckWindow *win; WnckApplication *app; win = wnck_screen_get_active_window (evra->screen); if (win) { app = wnck_window_get_application (win); if (! g_ascii_strcasecmp (appident, wnck_application_get_name (app))) return TRUE; } return FALSE; } void evra_send_key (EvraData *evra, gchar *keysymname) { KeySym keysym; keysym = XStringToKeysym (keysymname); if (keysym == NoSymbol) return; XTestGrabControl (evra->display, True); XTestFakeKeyEvent (evra->display, XKeysymToKeycode (evra->display, keysym), True, CurrentTime); XFlush (evra->display); XTestFakeKeyEvent (evra->display, XKeysymToKeycode (evra->display, keysym), False, CurrentTime); XTestGrabControl (evra->display, False); XFlush (evra->display); } void evra_set_feedback (EvraData *evra, gdouble feedback) { gint static_brightness, pulse_speed, pulse_table; gint pulse_asleep, pulse_awake; GIOStatus status; struct input_event ev; memset (&ev, 0, sizeof (struct input_event)); static_brightness = ((int) (rint (feedback * 255) + 0.5) & 0xFF); pulse_speed = 256; pulse_table = 0; pulse_asleep = FALSE; pulse_awake = FALSE; ev.type = EV_MSC; ev.code = MSC_PULSELED; ev.value = static_brightness | (pulse_speed << 8) | (pulse_table << 17) | (pulse_asleep << 19) | (pulse_awake << 20); status = write (evra->input_fd, &ev, sizeof (struct input_event)); if (status != sizeof (struct input_event)) g_printerr ("write failed: %s\n", strerror (errno)); } gboolean evra_update_gimp_opacity (EvraData *evra) { static gint intopacity = 100; if (intopacity != (gint) (rint (evra->gimp_opacity * 100) + 0.5)) { gint i; if (intopacity <= 50) { for (i = 0; i < 5; i++) evra_send_key (evra, "Down"); intopacity = (gint) (rint (evra->gimp_opacity * 100) + 0.5); for (i = 0; i < intopacity / 10; i++) evra_send_key (evra, "Up"); for (i = 0; i < intopacity % 10; i++) evra_send_key (evra, "Right"); } else { for (i = 0; i < 5; i++) evra_send_key (evra, "Up"); intopacity = (gint) (rint (evra->gimp_opacity * 100) + 0.5); for (i = 0; i < (100 - intopacity) / 10; i++) evra_send_key (evra, "Down"); for (i = 0; i < (100 - intopacity) % 10; i++) evra_send_key (evra, "Left"); } evra_set_feedback (evra, evra->gimp_opacity); } return TRUE; } void evra_dispatch_event (EvraData *evra, EvraEvent event) { if (evra_test_application (evra, "The GIMP")) { switch (event) { case EVRA_TURN_LEFT: evra->gimp_opacity = CLAMP (evra->gimp_opacity - 0.01, 0.0, 1.0); break; case EVRA_TURN_RIGHT: evra->gimp_opacity = CLAMP (evra->gimp_opacity + 0.01, 0.0, 1.0); break; default: break; } /* rely on the idle func to update */ } else if (evra_test_application (evra, "Galeon")) { switch (event) { case EVRA_TURN_LEFT: evra_send_key (evra, "Up"); break; case EVRA_TURN_RIGHT: evra_send_key (evra, "Down"); break; default: break; } } else { switch (event) { case EVRA_TURN_LEFT: evra_send_key (evra, "Left"); break; case EVRA_TURN_RIGHT: evra_send_key (evra, "Right"); break; default: break; } } } static const gchar * valid_prefix[2] = { "Griffin PowerMate", "Griffin SoundKnob" }; static gint evra_powermate_peek (const gchar *dev, gint mode) { gint fd; gint i; gchar name[255]; fd = open (dev, mode); if (fd < 0) { g_printerr ("Unable to open \"%s\": %s\n", dev, g_strerror (errno)); return -1; } if (ioctl (fd, EVIOCGNAME (sizeof (name)), name) < 0) { g_printerr ("\"%s\": EVIOCGNAME failed: %s\n", dev, g_strerror (errno)); close (fd); return -1; } /* we only can handle the Powermate Family of input devices */ for (i = 0; i < G_N_ELEMENTS (valid_prefix); i++) if (0 == strncasecmp (name, valid_prefix[i], strlen (valid_prefix[i]))) return fd; g_printerr ("Unable to use %s: found %s instead of Powermate\n", dev, name); close(fd); return -1; } gint evra_powermate_open (gint mode) { gchar devname[256]; gint i, r; for (i = 0; i < NUM_EVENT_DEVICES; i++) { g_sprintf (devname, "/dev/input/event%d", i); r = evra_powermate_peek (devname, mode); if (r >= 0) return r; } return -1; } gboolean evra_powermate_read_event (GIOChannel *ioc, GIOCondition cond, gpointer user_data) { gint n_bytes; GIOStatus status; struct input_event ev[1]; gint count; status = g_io_channel_read_chars (ioc, (gchar *) ev, sizeof (struct input_event), &n_bytes, NULL); if (status == G_IO_STATUS_NORMAL) { switch (ev->type) { case EV_REL: for (count = ev->value; count < 0; count++) evra_dispatch_event (evra, EVRA_TURN_LEFT); for (count = ev->value; count > 0; count--) evra_dispatch_event (evra, EVRA_TURN_RIGHT); break; default: break; } } return TRUE; } gint main (gint argc, gchar *argv[]) { evra = g_new (EvraData, 1); gtk_init (&argc, &argv); evra->screen = wnck_screen_get_default (); evra->gimp_opacity = 1.0; if ((evra->display = XOpenDisplay (XDisplayName (NULL))) == NULL) { g_printerr ("Can't connect to X display"); return 1; } evra->input_fd = evra_powermate_open (O_RDWR); if (evra->input_fd >= 0) { evra->ioc = g_io_channel_unix_new (evra->input_fd); g_io_channel_set_encoding (evra->ioc, NULL, NULL); evra->event_source_id = g_io_add_watch (evra->ioc, G_IO_IN, evra_powermate_read_event, (gpointer) evra); /* g_signal_connect (G_OBJECT (evra->screen), "active_window_changed", G_CALLBACK (evra_print_window), NULL); */ evra->mainloop = g_main_loop_new (NULL, FALSE); g_timeout_add (100, (GSourceFunc) evra_update_gimp_opacity, evra); g_main_loop_run (evra->mainloop); return 0; } return 1; }