blob: d6c517450d9ffe701a7cb9e1ef32faae45a7739e [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by the GNU General Public License v2.
//
// Partially adapted from evtest:
// http://cgit.freedesktop.org/evtest
//
// Copyright (c) 1999-2000 Vojtech Pavlik
// Copyright (c) 2009-2011 Red Hat, Inc
#define _GNU_SOURCE // for asprintf
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x) ((x)%BITS_PER_LONG)
#define BIT(x) (1UL<<OFF(x))
#define LONG(x) ((x)/BITS_PER_LONG)
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define LONG_BITS (sizeof(long) * 8)
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
#define DEV_INPUT_EVENT "/dev/input"
#define EVENT_DEV_NAME "event"
#ifndef EV_SYN
#define EV_SYN 0
#endif
#ifndef SYN_MT_REPORT
#define SYN_MT_REPORT 2
#endif
#ifndef ABS_MT_SLOT
#define ABS_MT_SLOT 47
#endif
/* Set clockid to be used for timestamps */
#ifndef EVIOCSCLOCKID
#define EVIOCSCLOCKID _IOW('E', 0xa0, int)
#endif
#define NAME_ELEMENT(element) [element] = #element
static const char * const events[EV_MAX + 1] = {
[0 ... EV_MAX] = NULL,
NAME_ELEMENT(EV_SYN), NAME_ELEMENT(EV_KEY), NAME_ELEMENT(EV_REL),
NAME_ELEMENT(EV_ABS), NAME_ELEMENT(EV_MSC), NAME_ELEMENT(EV_LED),
NAME_ELEMENT(EV_SND), NAME_ELEMENT(EV_REP), NAME_ELEMENT(EV_FF),
NAME_ELEMENT(EV_PWR), NAME_ELEMENT(EV_FF_STATUS), NAME_ELEMENT(EV_SW),
};
static const char * const keys[KEY_MAX + 1] = {
[0 ... KEY_MAX] = NULL,
NAME_ELEMENT(KEY_RESERVED), NAME_ELEMENT(KEY_ESC),
NAME_ELEMENT(KEY_1), NAME_ELEMENT(KEY_2), NAME_ELEMENT(KEY_3),
NAME_ELEMENT(KEY_4), NAME_ELEMENT(KEY_5), NAME_ELEMENT(KEY_6),
NAME_ELEMENT(KEY_7), NAME_ELEMENT(KEY_8), NAME_ELEMENT(KEY_9),
NAME_ELEMENT(KEY_0), NAME_ELEMENT(KEY_MINUS), NAME_ELEMENT(KEY_EQUAL),
NAME_ELEMENT(KEY_BACKSPACE), NAME_ELEMENT(KEY_TAB), NAME_ELEMENT(KEY_Q),
NAME_ELEMENT(KEY_W), NAME_ELEMENT(KEY_E), NAME_ELEMENT(KEY_R),
NAME_ELEMENT(KEY_T), NAME_ELEMENT(KEY_Y), NAME_ELEMENT(KEY_U),
NAME_ELEMENT(KEY_I), NAME_ELEMENT(KEY_O), NAME_ELEMENT(KEY_P),
NAME_ELEMENT(KEY_LEFTBRACE), NAME_ELEMENT(KEY_RIGHTBRACE),
NAME_ELEMENT(KEY_ENTER), NAME_ELEMENT(KEY_LEFTCTRL), NAME_ELEMENT(KEY_A),
NAME_ELEMENT(KEY_S), NAME_ELEMENT(KEY_D), NAME_ELEMENT(KEY_F),
NAME_ELEMENT(KEY_G), NAME_ELEMENT(KEY_H), NAME_ELEMENT(KEY_J),
NAME_ELEMENT(KEY_K), NAME_ELEMENT(KEY_L), NAME_ELEMENT(KEY_SEMICOLON),
NAME_ELEMENT(KEY_APOSTROPHE), NAME_ELEMENT(KEY_GRAVE),
NAME_ELEMENT(KEY_LEFTSHIFT), NAME_ELEMENT(KEY_BACKSLASH),
NAME_ELEMENT(KEY_Z), NAME_ELEMENT(KEY_X), NAME_ELEMENT(KEY_C),
NAME_ELEMENT(KEY_V), NAME_ELEMENT(KEY_B), NAME_ELEMENT(KEY_N),
NAME_ELEMENT(KEY_M), NAME_ELEMENT(KEY_COMMA), NAME_ELEMENT(KEY_DOT),
NAME_ELEMENT(KEY_SLASH), NAME_ELEMENT(KEY_RIGHTSHIFT),
NAME_ELEMENT(KEY_KPASTERISK), NAME_ELEMENT(KEY_LEFTALT),
NAME_ELEMENT(KEY_SPACE), NAME_ELEMENT(KEY_CAPSLOCK), NAME_ELEMENT(KEY_F1),
NAME_ELEMENT(KEY_F2), NAME_ELEMENT(KEY_F3), NAME_ELEMENT(KEY_F4),
NAME_ELEMENT(KEY_F5), NAME_ELEMENT(KEY_F6), NAME_ELEMENT(KEY_F7),
NAME_ELEMENT(KEY_F8), NAME_ELEMENT(KEY_F9), NAME_ELEMENT(KEY_F10),
NAME_ELEMENT(KEY_NUMLOCK), NAME_ELEMENT(KEY_SCROLLLOCK),
NAME_ELEMENT(KEY_KP7), NAME_ELEMENT(KEY_KP8), NAME_ELEMENT(KEY_KP9),
NAME_ELEMENT(KEY_KPMINUS), NAME_ELEMENT(KEY_KP4), NAME_ELEMENT(KEY_KP5),
NAME_ELEMENT(KEY_KP6), NAME_ELEMENT(KEY_KPPLUS), NAME_ELEMENT(KEY_KP1),
NAME_ELEMENT(KEY_KP2), NAME_ELEMENT(KEY_KP3), NAME_ELEMENT(KEY_KP0),
NAME_ELEMENT(KEY_KPDOT), NAME_ELEMENT(KEY_ZENKAKUHANKAKU),
NAME_ELEMENT(KEY_102ND), NAME_ELEMENT(KEY_F11), NAME_ELEMENT(KEY_F12),
NAME_ELEMENT(KEY_RO), NAME_ELEMENT(KEY_KATAKANA),
NAME_ELEMENT(KEY_HIRAGANA), NAME_ELEMENT(KEY_HENKAN),
NAME_ELEMENT(KEY_KATAKANAHIRAGANA), NAME_ELEMENT(KEY_MUHENKAN),
NAME_ELEMENT(KEY_KPJPCOMMA), NAME_ELEMENT(KEY_KPENTER),
NAME_ELEMENT(KEY_RIGHTCTRL), NAME_ELEMENT(KEY_KPSLASH),
NAME_ELEMENT(KEY_SYSRQ), NAME_ELEMENT(KEY_RIGHTALT),
NAME_ELEMENT(KEY_LINEFEED), NAME_ELEMENT(KEY_HOME), NAME_ELEMENT(KEY_UP),
NAME_ELEMENT(KEY_PAGEUP), NAME_ELEMENT(KEY_LEFT), NAME_ELEMENT(KEY_RIGHT),
NAME_ELEMENT(KEY_END), NAME_ELEMENT(KEY_DOWN), NAME_ELEMENT(KEY_PAGEDOWN),
NAME_ELEMENT(KEY_INSERT), NAME_ELEMENT(KEY_DELETE),
NAME_ELEMENT(KEY_MACRO), NAME_ELEMENT(KEY_MUTE),
NAME_ELEMENT(KEY_VOLUMEDOWN), NAME_ELEMENT(KEY_VOLUMEUP),
NAME_ELEMENT(KEY_POWER), NAME_ELEMENT(KEY_KPEQUAL),
NAME_ELEMENT(KEY_KPPLUSMINUS), NAME_ELEMENT(KEY_PAUSE),
NAME_ELEMENT(KEY_KPCOMMA), NAME_ELEMENT(KEY_HANGUEL),
NAME_ELEMENT(KEY_HANJA), NAME_ELEMENT(KEY_YEN),
NAME_ELEMENT(KEY_LEFTMETA), NAME_ELEMENT(KEY_RIGHTMETA),
NAME_ELEMENT(KEY_COMPOSE), NAME_ELEMENT(KEY_STOP),
NAME_ELEMENT(KEY_AGAIN), NAME_ELEMENT(KEY_PROPS), NAME_ELEMENT(KEY_UNDO),
NAME_ELEMENT(KEY_FRONT), NAME_ELEMENT(KEY_COPY), NAME_ELEMENT(KEY_OPEN),
NAME_ELEMENT(KEY_PASTE), NAME_ELEMENT(KEY_FIND), NAME_ELEMENT(KEY_CUT),
NAME_ELEMENT(KEY_HELP), NAME_ELEMENT(KEY_MENU), NAME_ELEMENT(KEY_CALC),
NAME_ELEMENT(KEY_SETUP), NAME_ELEMENT(KEY_SLEEP),
NAME_ELEMENT(KEY_WAKEUP), NAME_ELEMENT(KEY_FILE),
NAME_ELEMENT(KEY_SENDFILE), NAME_ELEMENT(KEY_DELETEFILE),
NAME_ELEMENT(KEY_XFER), NAME_ELEMENT(KEY_PROG1), NAME_ELEMENT(KEY_PROG2),
NAME_ELEMENT(KEY_WWW), NAME_ELEMENT(KEY_MSDOS), NAME_ELEMENT(KEY_COFFEE),
NAME_ELEMENT(KEY_DIRECTION), NAME_ELEMENT(KEY_CYCLEWINDOWS),
NAME_ELEMENT(KEY_MAIL), NAME_ELEMENT(KEY_BOOKMARKS),
NAME_ELEMENT(KEY_COMPUTER), NAME_ELEMENT(KEY_BACK),
NAME_ELEMENT(KEY_FORWARD), NAME_ELEMENT(KEY_CLOSECD),
NAME_ELEMENT(KEY_EJECTCD), NAME_ELEMENT(KEY_EJECTCLOSECD),
NAME_ELEMENT(KEY_NEXTSONG), NAME_ELEMENT(KEY_PLAYPAUSE),
NAME_ELEMENT(KEY_PREVIOUSSONG), NAME_ELEMENT(KEY_STOPCD),
NAME_ELEMENT(KEY_RECORD), NAME_ELEMENT(KEY_REWIND),
NAME_ELEMENT(KEY_PHONE), NAME_ELEMENT(KEY_ISO), NAME_ELEMENT(KEY_CONFIG),
NAME_ELEMENT(KEY_HOMEPAGE), NAME_ELEMENT(KEY_REFRESH),
NAME_ELEMENT(KEY_EXIT), NAME_ELEMENT(KEY_MOVE), NAME_ELEMENT(KEY_EDIT),
NAME_ELEMENT(KEY_SCROLLUP), NAME_ELEMENT(KEY_SCROLLDOWN),
NAME_ELEMENT(KEY_KPLEFTPAREN), NAME_ELEMENT(KEY_KPRIGHTPAREN),
NAME_ELEMENT(KEY_F13), NAME_ELEMENT(KEY_F14), NAME_ELEMENT(KEY_F15),
NAME_ELEMENT(KEY_F16), NAME_ELEMENT(KEY_F17), NAME_ELEMENT(KEY_F18),
NAME_ELEMENT(KEY_F19), NAME_ELEMENT(KEY_F20), NAME_ELEMENT(KEY_F21),
NAME_ELEMENT(KEY_F22), NAME_ELEMENT(KEY_F23), NAME_ELEMENT(KEY_F24),
NAME_ELEMENT(KEY_PLAYCD), NAME_ELEMENT(KEY_PAUSECD),
NAME_ELEMENT(KEY_PROG3), NAME_ELEMENT(KEY_PROG4),
NAME_ELEMENT(KEY_SUSPEND), NAME_ELEMENT(KEY_CLOSE),
NAME_ELEMENT(KEY_PLAY), NAME_ELEMENT(KEY_FASTFORWARD),
NAME_ELEMENT(KEY_BASSBOOST), NAME_ELEMENT(KEY_PRINT),
NAME_ELEMENT(KEY_HP), NAME_ELEMENT(KEY_CAMERA), NAME_ELEMENT(KEY_SOUND),
NAME_ELEMENT(KEY_QUESTION), NAME_ELEMENT(KEY_EMAIL),
NAME_ELEMENT(KEY_CHAT), NAME_ELEMENT(KEY_SEARCH),
NAME_ELEMENT(KEY_CONNECT), NAME_ELEMENT(KEY_FINANCE),
NAME_ELEMENT(KEY_SPORT), NAME_ELEMENT(KEY_SHOP),
NAME_ELEMENT(KEY_ALTERASE), NAME_ELEMENT(KEY_CANCEL),
NAME_ELEMENT(KEY_BRIGHTNESSDOWN), NAME_ELEMENT(KEY_BRIGHTNESSUP),
NAME_ELEMENT(KEY_MEDIA), NAME_ELEMENT(KEY_UNKNOWN), NAME_ELEMENT(KEY_OK),
NAME_ELEMENT(KEY_SELECT), NAME_ELEMENT(KEY_GOTO), NAME_ELEMENT(KEY_CLEAR),
NAME_ELEMENT(KEY_POWER2), NAME_ELEMENT(KEY_OPTION),
NAME_ELEMENT(KEY_INFO), NAME_ELEMENT(KEY_TIME), NAME_ELEMENT(KEY_VENDOR),
NAME_ELEMENT(KEY_ARCHIVE), NAME_ELEMENT(KEY_PROGRAM),
NAME_ELEMENT(KEY_CHANNEL), NAME_ELEMENT(KEY_FAVORITES),
NAME_ELEMENT(KEY_EPG), NAME_ELEMENT(KEY_PVR), NAME_ELEMENT(KEY_MHP),
NAME_ELEMENT(KEY_LANGUAGE), NAME_ELEMENT(KEY_TITLE),
NAME_ELEMENT(KEY_SUBTITLE), NAME_ELEMENT(KEY_ANGLE),
NAME_ELEMENT(KEY_ZOOM), NAME_ELEMENT(KEY_MODE),
NAME_ELEMENT(KEY_KEYBOARD), NAME_ELEMENT(KEY_SCREEN),
NAME_ELEMENT(KEY_PC), NAME_ELEMENT(KEY_TV), NAME_ELEMENT(KEY_TV2),
NAME_ELEMENT(KEY_VCR), NAME_ELEMENT(KEY_VCR2), NAME_ELEMENT(KEY_SAT),
NAME_ELEMENT(KEY_SAT2), NAME_ELEMENT(KEY_CD), NAME_ELEMENT(KEY_TAPE),
NAME_ELEMENT(KEY_RADIO), NAME_ELEMENT(KEY_TUNER),
NAME_ELEMENT(KEY_PLAYER), NAME_ELEMENT(KEY_TEXT), NAME_ELEMENT(KEY_DVD),
NAME_ELEMENT(KEY_AUX), NAME_ELEMENT(KEY_MP3), NAME_ELEMENT(KEY_AUDIO),
NAME_ELEMENT(KEY_VIDEO), NAME_ELEMENT(KEY_DIRECTORY),
NAME_ELEMENT(KEY_LIST), NAME_ELEMENT(KEY_MEMO),
NAME_ELEMENT(KEY_CALENDAR), NAME_ELEMENT(KEY_RED),
NAME_ELEMENT(KEY_GREEN), NAME_ELEMENT(KEY_YELLOW), NAME_ELEMENT(KEY_BLUE),
NAME_ELEMENT(KEY_CHANNELUP), NAME_ELEMENT(KEY_CHANNELDOWN),
NAME_ELEMENT(KEY_FIRST), NAME_ELEMENT(KEY_LAST), NAME_ELEMENT(KEY_AB),
NAME_ELEMENT(KEY_NEXT), NAME_ELEMENT(KEY_RESTART), NAME_ELEMENT(KEY_SLOW),
NAME_ELEMENT(KEY_SHUFFLE), NAME_ELEMENT(KEY_BREAK),
NAME_ELEMENT(KEY_PREVIOUS), NAME_ELEMENT(KEY_DIGITS),
NAME_ELEMENT(KEY_TEEN), NAME_ELEMENT(KEY_TWEN), NAME_ELEMENT(KEY_DEL_EOL),
NAME_ELEMENT(KEY_DEL_EOS), NAME_ELEMENT(KEY_INS_LINE),
NAME_ELEMENT(KEY_DEL_LINE), NAME_ELEMENT(KEY_VIDEOPHONE),
NAME_ELEMENT(KEY_GAMES), NAME_ELEMENT(KEY_ZOOMIN),
NAME_ELEMENT(KEY_ZOOMOUT), NAME_ELEMENT(KEY_ZOOMRESET),
NAME_ELEMENT(KEY_WORDPROCESSOR), NAME_ELEMENT(KEY_EDITOR),
NAME_ELEMENT(KEY_SPREADSHEET), NAME_ELEMENT(KEY_GRAPHICSEDITOR),
NAME_ELEMENT(KEY_PRESENTATION), NAME_ELEMENT(KEY_DATABASE),
NAME_ELEMENT(KEY_NEWS), NAME_ELEMENT(KEY_VOICEMAIL),
NAME_ELEMENT(KEY_ADDRESSBOOK), NAME_ELEMENT(KEY_MESSENGER),
NAME_ELEMENT(KEY_DISPLAYTOGGLE), NAME_ELEMENT(KEY_SPELLCHECK),
NAME_ELEMENT(KEY_LOGOFF), NAME_ELEMENT(KEY_DOLLAR),
NAME_ELEMENT(KEY_EURO), NAME_ELEMENT(KEY_FRAMEBACK),
NAME_ELEMENT(KEY_FRAMEFORWARD), NAME_ELEMENT(KEY_CONTEXT_MENU),
NAME_ELEMENT(KEY_MEDIA_REPEAT), NAME_ELEMENT(KEY_DEL_EOL),
NAME_ELEMENT(KEY_DEL_EOS), NAME_ELEMENT(KEY_INS_LINE),
NAME_ELEMENT(KEY_DEL_LINE), NAME_ELEMENT(KEY_FN),
NAME_ELEMENT(KEY_FN_ESC), NAME_ELEMENT(KEY_FN_F1),
NAME_ELEMENT(KEY_FN_F2), NAME_ELEMENT(KEY_FN_F3), NAME_ELEMENT(KEY_FN_F4),
NAME_ELEMENT(KEY_FN_F5), NAME_ELEMENT(KEY_FN_F6), NAME_ELEMENT(KEY_FN_F7),
NAME_ELEMENT(KEY_FN_F8), NAME_ELEMENT(KEY_FN_F9),
NAME_ELEMENT(KEY_FN_F10), NAME_ELEMENT(KEY_FN_F11),
NAME_ELEMENT(KEY_FN_F12), NAME_ELEMENT(KEY_FN_1), NAME_ELEMENT(KEY_FN_2),
NAME_ELEMENT(KEY_FN_D), NAME_ELEMENT(KEY_FN_E), NAME_ELEMENT(KEY_FN_F),
NAME_ELEMENT(KEY_FN_S), NAME_ELEMENT(KEY_FN_B),
NAME_ELEMENT(KEY_BRL_DOT1), NAME_ELEMENT(KEY_BRL_DOT2),
NAME_ELEMENT(KEY_BRL_DOT3), NAME_ELEMENT(KEY_BRL_DOT4),
NAME_ELEMENT(KEY_BRL_DOT5), NAME_ELEMENT(KEY_BRL_DOT6),
NAME_ELEMENT(KEY_BRL_DOT7), NAME_ELEMENT(KEY_BRL_DOT8),
NAME_ELEMENT(KEY_BRL_DOT9), NAME_ELEMENT(KEY_BRL_DOT10),
NAME_ELEMENT(KEY_NUMERIC_0), NAME_ELEMENT(KEY_NUMERIC_1),
NAME_ELEMENT(KEY_NUMERIC_2), NAME_ELEMENT(KEY_NUMERIC_3),
NAME_ELEMENT(KEY_NUMERIC_4), NAME_ELEMENT(KEY_NUMERIC_5),
NAME_ELEMENT(KEY_NUMERIC_6), NAME_ELEMENT(KEY_NUMERIC_7),
NAME_ELEMENT(KEY_NUMERIC_8), NAME_ELEMENT(KEY_NUMERIC_9),
NAME_ELEMENT(KEY_NUMERIC_STAR), NAME_ELEMENT(KEY_NUMERIC_POUND),
NAME_ELEMENT(KEY_BATTERY), NAME_ELEMENT(KEY_BLUETOOTH),
NAME_ELEMENT(KEY_BRIGHTNESS_CYCLE), NAME_ELEMENT(KEY_BRIGHTNESS_ZERO),
NAME_ELEMENT(KEY_DASHBOARD), NAME_ELEMENT(KEY_DISPLAY_OFF),
NAME_ELEMENT(KEY_DOCUMENTS), NAME_ELEMENT(KEY_FORWARDMAIL),
NAME_ELEMENT(KEY_NEW), NAME_ELEMENT(KEY_KBDILLUMDOWN),
NAME_ELEMENT(KEY_KBDILLUMUP), NAME_ELEMENT(KEY_KBDILLUMTOGGLE),
NAME_ELEMENT(KEY_REDO), NAME_ELEMENT(KEY_REPLY), NAME_ELEMENT(KEY_SAVE),
NAME_ELEMENT(KEY_SCALE), NAME_ELEMENT(KEY_SEND),
NAME_ELEMENT(KEY_SCREENLOCK), NAME_ELEMENT(KEY_SWITCHVIDEOMODE),
NAME_ELEMENT(KEY_UWB), NAME_ELEMENT(KEY_VIDEO_NEXT),
NAME_ELEMENT(KEY_VIDEO_PREV), NAME_ELEMENT(KEY_WIMAX),
NAME_ELEMENT(KEY_WLAN),
#ifdef KEY_RFKILL
NAME_ELEMENT(KEY_RFKILL),
#endif
#ifdef KEY_WPS_BUTTON
NAME_ELEMENT(KEY_WPS_BUTTON),
#endif
#ifdef KEY_TOUCHPAD_TOGGLE
NAME_ELEMENT(KEY_TOUCHPAD_TOGGLE),
NAME_ELEMENT(KEY_TOUCHPAD_ON),
NAME_ELEMENT(KEY_TOUCHPAD_OFF),
#endif
NAME_ELEMENT(BTN_0), NAME_ELEMENT(BTN_1), NAME_ELEMENT(BTN_2),
NAME_ELEMENT(BTN_3), NAME_ELEMENT(BTN_4), NAME_ELEMENT(BTN_5),
NAME_ELEMENT(BTN_6), NAME_ELEMENT(BTN_7), NAME_ELEMENT(BTN_8),
NAME_ELEMENT(BTN_9), NAME_ELEMENT(BTN_LEFT), NAME_ELEMENT(BTN_RIGHT),
NAME_ELEMENT(BTN_MIDDLE), NAME_ELEMENT(BTN_SIDE),
NAME_ELEMENT(BTN_EXTRA), NAME_ELEMENT(BTN_FORWARD),
NAME_ELEMENT(BTN_BACK), NAME_ELEMENT(BTN_TASK), NAME_ELEMENT(BTN_TRIGGER),
NAME_ELEMENT(BTN_THUMB), NAME_ELEMENT(BTN_THUMB2), NAME_ELEMENT(BTN_TOP),
NAME_ELEMENT(BTN_TOP2), NAME_ELEMENT(BTN_PINKIE), NAME_ELEMENT(BTN_BASE),
NAME_ELEMENT(BTN_BASE2), NAME_ELEMENT(BTN_BASE3), NAME_ELEMENT(BTN_BASE4),
NAME_ELEMENT(BTN_BASE5), NAME_ELEMENT(BTN_BASE6), NAME_ELEMENT(BTN_DEAD),
NAME_ELEMENT(BTN_A), NAME_ELEMENT(BTN_B), NAME_ELEMENT(BTN_C),
NAME_ELEMENT(BTN_X), NAME_ELEMENT(BTN_Y), NAME_ELEMENT(BTN_Z),
NAME_ELEMENT(BTN_TL), NAME_ELEMENT(BTN_TR), NAME_ELEMENT(BTN_TL2),
NAME_ELEMENT(BTN_TR2), NAME_ELEMENT(BTN_SELECT), NAME_ELEMENT(BTN_START),
NAME_ELEMENT(BTN_MODE), NAME_ELEMENT(BTN_THUMBL),
NAME_ELEMENT(BTN_THUMBR), NAME_ELEMENT(BTN_TOOL_PEN),
NAME_ELEMENT(BTN_TOOL_RUBBER), NAME_ELEMENT(BTN_TOOL_BRUSH),
NAME_ELEMENT(BTN_TOOL_PENCIL), NAME_ELEMENT(BTN_TOOL_AIRBRUSH),
NAME_ELEMENT(BTN_TOOL_FINGER), NAME_ELEMENT(BTN_TOOL_MOUSE),
NAME_ELEMENT(BTN_TOOL_LENS), NAME_ELEMENT(BTN_TOUCH),
NAME_ELEMENT(BTN_STYLUS), NAME_ELEMENT(BTN_STYLUS2),
NAME_ELEMENT(BTN_TOOL_DOUBLETAP), NAME_ELEMENT(BTN_TOOL_TRIPLETAP),
NAME_ELEMENT(BTN_TOOL_QUADTAP), NAME_ELEMENT(BTN_GEAR_DOWN),
NAME_ELEMENT(BTN_GEAR_UP),
#ifdef BTN_TRIGGER_HAPPY
NAME_ELEMENT(BTN_TRIGGER_HAPPY1), NAME_ELEMENT(BTN_TRIGGER_HAPPY11),
NAME_ELEMENT(BTN_TRIGGER_HAPPY2), NAME_ELEMENT(BTN_TRIGGER_HAPPY12),
NAME_ELEMENT(BTN_TRIGGER_HAPPY3), NAME_ELEMENT(BTN_TRIGGER_HAPPY13),
NAME_ELEMENT(BTN_TRIGGER_HAPPY4), NAME_ELEMENT(BTN_TRIGGER_HAPPY14),
NAME_ELEMENT(BTN_TRIGGER_HAPPY5), NAME_ELEMENT(BTN_TRIGGER_HAPPY15),
NAME_ELEMENT(BTN_TRIGGER_HAPPY6), NAME_ELEMENT(BTN_TRIGGER_HAPPY16),
NAME_ELEMENT(BTN_TRIGGER_HAPPY7), NAME_ELEMENT(BTN_TRIGGER_HAPPY17),
NAME_ELEMENT(BTN_TRIGGER_HAPPY8), NAME_ELEMENT(BTN_TRIGGER_HAPPY18),
NAME_ELEMENT(BTN_TRIGGER_HAPPY9), NAME_ELEMENT(BTN_TRIGGER_HAPPY19),
NAME_ELEMENT(BTN_TRIGGER_HAPPY10), NAME_ELEMENT(BTN_TRIGGER_HAPPY20),
NAME_ELEMENT(BTN_TRIGGER_HAPPY21), NAME_ELEMENT(BTN_TRIGGER_HAPPY31),
NAME_ELEMENT(BTN_TRIGGER_HAPPY22), NAME_ELEMENT(BTN_TRIGGER_HAPPY32),
NAME_ELEMENT(BTN_TRIGGER_HAPPY23), NAME_ELEMENT(BTN_TRIGGER_HAPPY33),
NAME_ELEMENT(BTN_TRIGGER_HAPPY24), NAME_ELEMENT(BTN_TRIGGER_HAPPY34),
NAME_ELEMENT(BTN_TRIGGER_HAPPY25), NAME_ELEMENT(BTN_TRIGGER_HAPPY35),
NAME_ELEMENT(BTN_TRIGGER_HAPPY26), NAME_ELEMENT(BTN_TRIGGER_HAPPY36),
NAME_ELEMENT(BTN_TRIGGER_HAPPY27), NAME_ELEMENT(BTN_TRIGGER_HAPPY37),
NAME_ELEMENT(BTN_TRIGGER_HAPPY28), NAME_ELEMENT(BTN_TRIGGER_HAPPY38),
NAME_ELEMENT(BTN_TRIGGER_HAPPY29), NAME_ELEMENT(BTN_TRIGGER_HAPPY39),
NAME_ELEMENT(BTN_TRIGGER_HAPPY30), NAME_ELEMENT(BTN_TRIGGER_HAPPY40),
#endif
};
static const char * const absval[6] = {
"Value", "Min ", "Max ", "Fuzz ", "Flat ", "Resolution " };
static const char * const relatives[REL_MAX + 1] = {
[0 ... REL_MAX] = NULL,
NAME_ELEMENT(REL_X), NAME_ELEMENT(REL_Y), NAME_ELEMENT(REL_Z),
NAME_ELEMENT(REL_RX), NAME_ELEMENT(REL_RY), NAME_ELEMENT(REL_RZ),
NAME_ELEMENT(REL_HWHEEL), NAME_ELEMENT(REL_DIAL), NAME_ELEMENT(REL_WHEEL),
NAME_ELEMENT(REL_MISC),
};
static const char * const absolutes[ABS_MAX + 1] = {
[0 ... ABS_MAX] = NULL,
NAME_ELEMENT(ABS_X), NAME_ELEMENT(ABS_Y), NAME_ELEMENT(ABS_Z),
NAME_ELEMENT(ABS_RX), NAME_ELEMENT(ABS_RY), NAME_ELEMENT(ABS_RZ),
NAME_ELEMENT(ABS_THROTTLE), NAME_ELEMENT(ABS_RUDDER),
NAME_ELEMENT(ABS_WHEEL), NAME_ELEMENT(ABS_GAS), NAME_ELEMENT(ABS_BRAKE),
NAME_ELEMENT(ABS_HAT0X), NAME_ELEMENT(ABS_HAT0Y), NAME_ELEMENT(ABS_HAT1X),
NAME_ELEMENT(ABS_HAT1Y), NAME_ELEMENT(ABS_HAT2X), NAME_ELEMENT(ABS_HAT2Y),
NAME_ELEMENT(ABS_HAT3X), NAME_ELEMENT(ABS_HAT3Y),
NAME_ELEMENT(ABS_PRESSURE), NAME_ELEMENT(ABS_DISTANCE),
NAME_ELEMENT(ABS_TILT_X), NAME_ELEMENT(ABS_TILT_Y),
NAME_ELEMENT(ABS_TOOL_WIDTH), NAME_ELEMENT(ABS_VOLUME),
NAME_ELEMENT(ABS_MISC),
#ifdef ABS_MT_BLOB_ID
NAME_ELEMENT(ABS_MT_TOUCH_MAJOR),
NAME_ELEMENT(ABS_MT_TOUCH_MINOR),
NAME_ELEMENT(ABS_MT_WIDTH_MAJOR),
NAME_ELEMENT(ABS_MT_WIDTH_MINOR),
NAME_ELEMENT(ABS_MT_ORIENTATION),
NAME_ELEMENT(ABS_MT_POSITION_X),
NAME_ELEMENT(ABS_MT_POSITION_Y),
NAME_ELEMENT(ABS_MT_TOOL_TYPE),
NAME_ELEMENT(ABS_MT_BLOB_ID),
#endif
#ifdef ABS_MT_TRACKING_ID
NAME_ELEMENT(ABS_MT_TRACKING_ID),
#endif
#ifdef ABS_MT_PRESSURE
NAME_ELEMENT(ABS_MT_PRESSURE),
#endif
#ifdef ABS_MT_SLOT
NAME_ELEMENT(ABS_MT_SLOT),
#endif
};
static const char * const misc[MSC_MAX + 1] = {
[0 ... MSC_MAX] = NULL,
NAME_ELEMENT(MSC_SERIAL), NAME_ELEMENT(MSC_PULSELED),
NAME_ELEMENT(MSC_GESTURE), NAME_ELEMENT(MSC_RAW),
NAME_ELEMENT(MSC_SCAN),
};
static const char * const leds[LED_MAX + 1] = {
[0 ... LED_MAX] = NULL,
NAME_ELEMENT(LED_NUML), NAME_ELEMENT(LED_CAPSL),
NAME_ELEMENT(LED_SCROLLL), NAME_ELEMENT(LED_COMPOSE),
NAME_ELEMENT(LED_KANA), NAME_ELEMENT(LED_SLEEP),
NAME_ELEMENT(LED_SUSPEND), NAME_ELEMENT(LED_MUTE), NAME_ELEMENT(LED_MISC),
};
static const char * const repeats[REP_MAX + 1] = {
[0 ... REP_MAX] = NULL,
NAME_ELEMENT(REP_DELAY), NAME_ELEMENT(REP_PERIOD)
};
static const char * const sounds[SND_MAX + 1] = {
[0 ... SND_MAX] = NULL,
NAME_ELEMENT(SND_CLICK), NAME_ELEMENT(SND_BELL), NAME_ELEMENT(SND_TONE)
};
static const char * const syns[3] = {
NAME_ELEMENT(SYN_REPORT), NAME_ELEMENT(SYN_CONFIG),
#ifdef SYN_MT_REPORT
NAME_ELEMENT(SYN_MT_REPORT)
#endif
};
static const char * const switches[SW_MAX + 1] = {
[0 ... SW_MAX] = NULL,
NAME_ELEMENT(SW_LID), NAME_ELEMENT(SW_TABLET_MODE),
NAME_ELEMENT(SW_HEADPHONE_INSERT), NAME_ELEMENT(SW_RFKILL_ALL),
NAME_ELEMENT(SW_MICROPHONE_INSERT), NAME_ELEMENT(SW_DOCK),
NAME_ELEMENT(SW_LINEOUT_INSERT), NAME_ELEMENT(SW_JACK_PHYSICAL_INSERT),
#ifdef SW_VIDEOOUT_INSERT
NAME_ELEMENT(SW_VIDEOOUT_INSERT),
#endif
#ifdef SW_CAMERA_LENS_COVER
NAME_ELEMENT(SW_CAMERA_LENS_COVER),
NAME_ELEMENT(SW_KEYPAD_SLIDE),
NAME_ELEMENT(SW_FRONT_PROXIMITY),
#endif
#ifdef SW_ROTATE_LOCK
NAME_ELEMENT(SW_ROTATE_LOCK),
#endif
};
static const char * const force[FF_MAX + 1] = {
[0 ... FF_MAX] = NULL,
NAME_ELEMENT(FF_RUMBLE), NAME_ELEMENT(FF_PERIODIC),
NAME_ELEMENT(FF_CONSTANT), NAME_ELEMENT(FF_SPRING),
NAME_ELEMENT(FF_FRICTION), NAME_ELEMENT(FF_DAMPER),
NAME_ELEMENT(FF_INERTIA), NAME_ELEMENT(FF_RAMP), NAME_ELEMENT(FF_SQUARE),
NAME_ELEMENT(FF_TRIANGLE), NAME_ELEMENT(FF_SINE), NAME_ELEMENT(FF_SAW_UP),
NAME_ELEMENT(FF_SAW_DOWN), NAME_ELEMENT(FF_CUSTOM), NAME_ELEMENT(FF_GAIN),
NAME_ELEMENT(FF_AUTOCENTER),
};
static const char * const forcestatus[FF_STATUS_MAX + 1] = {
[0 ... FF_STATUS_MAX] = NULL,
NAME_ELEMENT(FF_STATUS_STOPPED), NAME_ELEMENT(FF_STATUS_PLAYING),
};
static const char * const * const names[EV_MAX + 1] = {
[0 ... EV_MAX] = NULL,
[EV_SYN] = events, [EV_KEY] = keys, [EV_REL] = relatives,
[EV_ABS] = absolutes, [EV_MSC] = misc, [EV_LED] = leds, [EV_SND] = sounds,
[EV_REP] = repeats, [EV_SW] = switches, [EV_FF] = force,
[EV_FF_STATUS] = forcestatus,
};
struct mt_slot {
int track_id;
int touch_major;
int touch_minor;
int width_major;
int width_minor;
int orientation;
int x;
int y;
int pressure;
int tool_type;
};
struct mt_state {
int current;
struct mt_slot *slot;
};
// Program Options
bool monotonic = true;
bool persist = true;
static Display *dpy;
static unsigned long blackColor;
static Window w;
static GC gc;
static Colormap colormap;
const char * const color_str[] = {
"#0000FF", // Blue
"#00FF00", // Green
"#FF0000", // Red
"#FFFF00", // Yellow
"#00FFFF", // Cyan
"#FF00FF", // Magenta
"#FFFFFF", // White
"#FF8040", // Orange
"#804000", // Brown
"#808080", // Grey
};
#define COLOR_COUNT ARRAY_SIZE(color_str)
static XColor color[COLOR_COUNT];
static int slot_min = 0;
static int slot_max = 10;
static int x_min = 0;
static int x_max = 0;
static int y_min = 0;
static int y_max = 0;
static int pressure_min = 0;
static int pressure_max = 255;
static bool semi_mt_device = false;
static unsigned int w_width;
static unsigned int w_height;
static Display *InitDisplay(const char *displayname) {
Window root;
int x, y;
unsigned int border_width;
unsigned int depth;
int i;
dpy = XOpenDisplay(displayname);
if (!dpy)
return NULL;
blackColor = BlackPixel(dpy, DefaultScreen(dpy));
// Create a window
w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
DisplayWidth(dpy, 0), DisplayHeight(dpy, 0),
0, blackColor, blackColor);
// We want to get MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
// "Map" the window (that is, make it appear on the screen)
XMapWindow(dpy, w);
// Create a "Graphics Context"
gc = XCreateGC(dpy, w, 0, NULL);
colormap = DefaultColormap(dpy, DefaultScreen(dpy));
for (i = 0; i < COLOR_COUNT; i++) {
XParseColor(dpy, colormap, color_str[i], &color[i]);
XAllocColor(dpy, colormap, &color[i]);
}
// Wait for the MapNotify event
while (1) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify)
break;
}
XGetGeometry(dpy, w, &root, &x, &y, &w_width, &w_height, &border_width,
&depth);
// TODO: Resize touch surface to 90% of screen, and draw a virtual bezel
// since Touchpads sometimes emit events outside advertised range.
return dpy;
}
static void MtSlotInit(struct mt_slot *s) {
memset(s, 0, sizeof(struct mt_slot));
s->track_id = -1;
}
static void MtSlotPaint(struct mt_slot *s) {
int x, y;
unsigned int width, height;
unsigned long forecolor;
if ((s->track_id == -1) || (s->pressure == 0))
return;
// TODO: Get really fancy and use touch_minor & orientation
// TODO: Get really really fancy, and use 'width_*'
width = 50 * (s->pressure - pressure_min) / (pressure_max - pressure_min);
height = 50 * (s->pressure - pressure_min) / (pressure_max - pressure_min);
// TODO: Update x/y_min/max on window resize
x = (s->x - x_min) * w_width / (x_max - x_min) - (width - 1) / 2;
y = (s->y - y_min) * w_height / (y_max - y_min) - (height - 1) / 2;
// TODO: Get fancy and adjust color intensity using pressure
forecolor = color[s->track_id % COLOR_COUNT].pixel;
XSetForeground(dpy, gc, forecolor);
XFillArc(dpy, w, gc, x, y, width, height, 0, 360 * 64);
}
static void MtStateInit(struct mt_state *s) {
int i;
s->current = 0;
s->slot = calloc(slot_max - slot_min + 1, sizeof(struct mt_slot));
for (i = slot_min; i <= slot_max; i++)
MtSlotInit(&s->slot[i - slot_min]);
}
static void MtStatePaint(struct mt_state *s) {
int i;
if (!persist)
XClearWindow(dpy, w);
for (i = slot_min; i <= slot_max; i++)
MtSlotPaint(&s->slot[i - slot_min]);
XFlush(dpy);
}
static void ProcessSynReport(struct mt_state *s, struct input_event *e) {
MtStatePaint(s);
}
static void ProcessKey(struct mt_state *s, struct input_event *e) {
if (e->code == BTN_LEFT)
XClearWindow(dpy, w);
}
// Update all active slots with the same ABS_PRESSURE value as it is a
// semi-mt device.
static void UpdateWithAbsPressure(struct mt_state *s, struct input_event *e) {
int i;
for (i = slot_min; i <= slot_max; i++) {
struct mt_slot *slt = &s->slot[i];
if (slt->track_id != -1)
slt->pressure = e->value;
}
}
static void ProcessAbs(struct mt_state *s, struct input_event *e) {
struct mt_slot *slot = &s->slot[s->current];
switch (e->code) {
case ABS_MT_SLOT:
s->current = e->value;
break;
case ABS_MT_TOUCH_MAJOR:
slot->touch_major = e->value;
break;
case ABS_MT_TOUCH_MINOR:
slot->touch_minor = e->value;
break;
case ABS_MT_WIDTH_MAJOR:
slot->width_major = e->value;
break;
case ABS_MT_WIDTH_MINOR:
slot->width_minor = e->value;
break;
case ABS_MT_ORIENTATION:
slot->orientation = e->value;
break;
case ABS_MT_POSITION_X:
slot->x = e->value;
break;
case ABS_MT_POSITION_Y:
slot->y = e->value;
break;
case ABS_MT_TOOL_TYPE:
slot->tool_type = e->value;
break;
case ABS_MT_BLOB_ID:
break;
case ABS_MT_TRACKING_ID:
slot->track_id = e->value;
break;
case ABS_MT_PRESSURE:
slot->pressure = e->value;
break;
case ABS_PRESSURE:
if (semi_mt_device)
UpdateWithAbsPressure(s, e);
break;
default:
break;
}
}
static void MtStateUpdate(struct mt_state *s, struct input_event *e) {
if (e->type == EV_SYN && e->code == SYN_REPORT)
ProcessSynReport(s, e);
else if (e->type == EV_KEY)
ProcessKey(s, e);
else if (e->type == EV_ABS)
ProcessAbs(s, e);
}
// Filter for the AutoDevProbe scandir on /dev/input.
//
// @param dir The current directory entry provided by scandir.
//
// @return Non-zero if the given directory entry starts with "event", or zero
// otherwise.
static int IsEventDevice(const struct dirent *dir) {
return strncmp(EVENT_DEV_NAME, dir->d_name, 5) == 0;
}
// Scans all /dev/input/event*, display them and ask the user which one to
// open.
//
// @return The event device file name of the device file selected. This
// string is allocated and must be freed by the caller.
static char *ScanDevices(void) {
struct dirent **namelist;
int i, ndev, devnum, rc;
char *filename;
ndev = scandir(DEV_INPUT_EVENT, &namelist, IsEventDevice, alphasort);
if (ndev <= 0)
return NULL;
fprintf(stderr, "Available devices:\n");
for (i = 0; i < ndev; i++)
{
char fname[64];
int fd = -1;
char name[256] = "???";
snprintf(fname, sizeof(fname), "%s/%s", DEV_INPUT_EVENT,
namelist[i]->d_name);
fd = open(fname, O_RDONLY);
if (fd < 0)
continue;
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
fprintf(stderr, "%s:\t%s\n", fname, name);
close(fd);
free(namelist[i]);
}
fprintf(stderr, "Select the device event number [0-%d]: ", ndev - 1);
rc = scanf("%d", &devnum);
if (rc != 1 || devnum >= ndev || devnum < 0)
return NULL;
rc = asprintf(&filename, "%s/%s%d",
DEV_INPUT_EVENT, EVENT_DEV_NAME, devnum);
if (rc == -1)
return NULL;
return filename;
}
// Print additional information for absolute axes (min/max, current value,
// etc.).
//
// @param fd The file descriptor to the device.
// @param axis The axis identifier (e.g. ABS_X).
static void PrintAbsData(int fd, int axis) {
int abs[6] = {0};
int k;
ioctl(fd, EVIOCGABS(axis), abs);
for (k = 0; k < 6; k++)
if ((k < 3) || abs[k]) {
printf(" %s %6d\n", absval[k], abs[k]);
if (k == 1) {
if (axis == ABS_MT_SLOT)
slot_min = abs[k];
else if (axis == ABS_MT_POSITION_X)
x_min = abs[k];
else if (axis == ABS_MT_POSITION_Y)
y_min = abs[k];
else if (axis == ABS_MT_PRESSURE)
pressure_min = abs[k];
else if (axis == ABS_PRESSURE)
pressure_min = abs[k];
} else if (k == 2) {
if (axis == ABS_MT_SLOT)
slot_max = abs[k];
else if (axis == ABS_MT_POSITION_X)
x_max = abs[k];
else if (axis == ABS_MT_POSITION_Y)
y_max = abs[k];
else if (axis == ABS_MT_PRESSURE)
pressure_max = abs[k];
else if (axis == ABS_PRESSURE)
pressure_max = abs[k];
}
}
}
// Test if the INPUT_PROP_SEMI_MT bit is set. If so, we will retrieve the
// pressure value from the element ABS_PRESSURE instead.
//
// @param fd The file descriptor to the device.
// @return true if the bit INPUT_PROP_SEMI_MT is set, or zero otherwise.
static bool IsSemiMtDevice(int fd) {
unsigned long prop_bitmask[NLONGS(INPUT_PROP_CNT)];
int len = ioctl(fd, EVIOCGPROP(sizeof(prop_bitmask)), prop_bitmask);
if (len < 0) {
printf("get properties error: %s\n", strerror(errno));
return false;
}
return test_bit(INPUT_PROP_SEMI_MT, prop_bitmask);
}
// Print static device information (no events). This information includes
// version numbers, device name and all bits supported by this device.
//
// @param fd The file descriptor to the device.
// @return 0 on success or 1 otherwise.
static int PrintDeviceInfo(int fd) {
int i, j;
int version;
unsigned short id[4];
char name[256] = "Unknown";
unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
if (ioctl(fd, EVIOCGVERSION, &version)) {
perror("mtplot: can't get version");
return 1;
}
printf("Input driver version is %d.%d.%d\n",
version >> 16, (version >> 8) & 0xff, version & 0xff);
ioctl(fd, EVIOCGID, id);
printf("Input device ID: bus 0x%x vendor 0x%x product 0x%x version 0x%x\n",
id[ID_BUS], id[ID_VENDOR], id[ID_PRODUCT], id[ID_VERSION]);
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
printf("Input device name: \"%s\"\n", name);
memset(bit, 0, sizeof(bit));
ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]);
printf("Supported events:\n");
for (i = 0; i < EV_MAX; i++)
if (test_bit(i, bit[0])) {
printf(" Event type %d (%s)\n", i, events[i] ? events[i] : "?");
if (!i)
continue;
ioctl(fd, EVIOCGBIT(i, KEY_MAX), bit[i]);
for (j = 0; j < KEY_MAX; j++)
if (test_bit(j, bit[i])) {
printf(" Event code %d (%s)\n", j,
names[i] ? (names[i][j] ? names[i][j] : "?") : "?");
if (i == EV_ABS)
PrintAbsData(fd, j);
}
}
return 0;
}
// Grab and immediately ungrab the device.
//
// @param fd The file descriptor to the device.
// @return 0 if the grab was successful, or 1 otherwise.
static int TestGrab(int fd) {
int rc;
rc = ioctl(fd, EVIOCGRAB, (void *)1);
if (!rc)
ioctl(fd, EVIOCGRAB, (void *)0);
return rc;
}
static void EnableMonotonicTimestamps(int fd)
{
unsigned int clk = CLOCK_MONOTONIC;
int ret = ioctl(fd, EVIOCSCLOCKID, &clk);
if (ret < 0)
fprintf(stderr, "Monotonic timestamps not supported in this kernel.\n");
}
static const struct option long_options[] = {
{ "display", required_argument, NULL, 'd' },
{ "monotonic", optional_argument, NULL, 'm' },
{ "persist", optional_argument, NULL, 'p' },
{ 0, },
};
// Print usage information.
static int Usage(void) {
printf("USAGE:\n");
printf(" %s /dev/input/eventX [--display DISPLAY] [--monotonic] [--persist]\n",
program_invocation_short_name);
printf("\n");
printf(" -m, --monotonic[=0|1] =1: requests input events with monotonic timestamps\n"
" if supported by the kernel (default)\n"
" =0: request input events with wallclock timestamps\n");
printf(" -p, --persist[=0|1] =1: display persistent trails for each contact (default)\n"
" =0: only display the current contact positions\n"
" To toggle at runtime press 'p'\n");
return EXIT_FAILURE;
}
int main(int argc, char **argv) {
const char *device = NULL;
char *displayname = NULL;
char *filename;
struct input_event ev[64];
int i, rd;
struct mt_state mt_state;
int fd, x11_fd, max_fd;
fd_set rdfs;
while (1) {
int c = getopt_long_only(argc, argv, "d:m:p:", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'd':
displayname = optarg;
break;
case 'm':
monotonic = (optarg) ? atoi(optarg) : true;
break;
case 'p':
persist = (optarg) ? atoi(optarg) : true;
break;
default: // Fall through case
printf("Unknown option '%c'\n", c);
case '?':
return Usage();
}
}
if (optind < argc)
device = argv[optind++];
if (!device) {
fprintf(stderr, "No device specified, trying to scan all of %s/%s*\n",
DEV_INPUT_EVENT, EVENT_DEV_NAME);
if (getuid() != 0)
fprintf(stderr, "Not running as root, no devices may be available.\n");
filename = ScanDevices();
if (!filename)
return Usage();
} else {
filename = strdup(device);
}
if (!filename)
return EXIT_FAILURE;
if ((fd = open(filename, O_RDONLY)) < 0) {
perror("mtplot");
if (errno == EACCES && getuid() != 0)
fprintf(stderr, "You do not have access to %s. Try "
"running as root instead.\n",
filename);
return EXIT_FAILURE;
}
free(filename);
if (!isatty(fileno(stdout)))
setbuf(stdout, NULL);
if (PrintDeviceInfo(fd))
return EXIT_FAILURE;
if (IsSemiMtDevice(fd))
semi_mt_device = true;
if (monotonic)
EnableMonotonicTimestamps(fd);
printf("Testing ... (interrupt to exit)\n");
if (TestGrab(fd)) {
printf("***********************************************\n");
printf(" This device is grabbed by another process.\n");
printf(" No events are available to mtplot while the\n");
printf(" other grab is active.\n");
printf(" In most cases, this is caused by an X driver,\n");
printf(" try VT-switching and re-run mtplot again.\n");
printf("***********************************************\n");
}
dpy = InitDisplay(displayname);
if (!dpy) {
fprintf (stderr, "mtplot: unable to open display '%s'\n",
XDisplayName (displayname));
return EXIT_FAILURE;
}
MtStateInit(&mt_state);
x11_fd = ConnectionNumber(dpy);
max_fd = (x11_fd > fd) ? x11_fd : fd;
while (1) {
int rc;
// Add device and X file descriptors to fd_set
FD_ZERO(&rdfs);
FD_SET(x11_fd, &rdfs);
FD_SET(fd, &rdfs);
rc = select(max_fd + 1, &rdfs, NULL, NULL, NULL);
if (rc == -1)
perror("select()");
if (FD_ISSET(fd, &rdfs)) {
rd = read(fd, ev, sizeof(struct input_event) * 64);
if (rd < (int) sizeof(struct input_event)) {
printf("expected %d bytes, got %d\n",
(int) sizeof(struct input_event), rd);
perror("\nmtplot: error reading");
return EXIT_FAILURE;
}
for (i = 0; i < rd / sizeof(struct input_event); i++) {
printf("Event: time %ld.%06ld, ", ev[i].time.tv_sec,
ev[i].time.tv_usec);
if (ev[i].type == EV_SYN) {
if (ev[i].code == SYN_MT_REPORT)
printf("++++++++++++++ %s ++++++++++++\n", syns[ev[i].code]);
else
printf("-------------- %s ------------\n", syns[ev[i].code]);
} else {
int type = ev[i].type;
int code = ev[i].code;
int value = ev[i].value;
printf("type %d (%s), code %d (%s), ", type, events[type] ?: "?",
code, names[type] ? (names[type][code] ?: "?") : "?");
if (type == EV_MSC && (code == MSC_RAW || code == MSC_SCAN))
printf("value %02x\n", value);
else
printf("value %d\n", value);
}
MtStateUpdate(&mt_state, &ev[i]);
}
}
if (FD_ISSET(x11_fd, &rdfs)) {
while (XPending(dpy)) {
XEvent ev;
XNextEvent(dpy, &ev);
if (ev.type == ConfigureNotify) {
XConfigureEvent xce = ev.xconfigure;
// Save new window dimensions
w_width = xce.width;
w_height = xce.height;
} else if (ev.type == KeyPress) {
XKeyEvent xke = ev.xkey;
KeySym ks = XLookupKeysym(&xke, 0);
if (ks == XK_Escape)
XClearWindow(dpy, w);
else if (ks == XK_p)
persist = !persist;
}
}
}
}
}