diff options
-rw-r--r-- | rawjoystick.c | 185 | ||||
-rw-r--r-- | rawmouse.c | 366 |
2 files changed, 551 insertions, 0 deletions
diff --git a/rawjoystick.c b/rawjoystick.c new file mode 100644 index 0000000..73dce1e --- /dev/null +++ b/rawjoystick.c @@ -0,0 +1,185 @@ +/* +'pd_joystick' (An external library for Miller Puckette's 'PD' software +adding PC and/or USB joystick control capabilities) + +Copyright (C) 2001 Joseph A. Sarlo + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +jsarlo@peabody.jhu.edu +*/ + +#include "m_pd.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#ifdef USB +#define JOYSTICK_DEVICE "/dev/input/js0" +#else +#define JOYSTICK_DEVICE "dev/js0" +#endif + +// scaling factor for output +#define DEF_SCALE 1 +// delay/refresh time in milliseconds +#define DEF_DELTIME 5 +// this object handles a maximum of 10 axes +#define MAX_AXIS_OUTS 10 +// input event types +#define JS_EVENT_BUTTON 0x01 +#define JS_EVENT_AXIS 0x02 +#define JS_EVENT_INIT 0x80 + +// ise ioctl() to get number of axes and buttons from device +#define JSIOCGAXES _IOR('j', 0x11, unsigned char) +#define JSIOCGBUTTONS _IOR('j', 0x12, unsigned char) + +struct js_event +{ + unsigned int time; + signed short value; + unsigned char type; + unsigned char number; +}; + +typedef struct _joystick +{ + t_object t_ob; + struct js_event x_joy_e; + int x_joy_fd; + float x_scale; + float x_translation; + t_outlet *x_axis_out[10]; + t_outlet *x_button_num_out; + t_outlet *x_button_val_out; + t_clock *x_clock; + double x_deltime; + unsigned char x_joy_buttons; + unsigned char x_joy_axes; +}t_joystick; + +t_class *joystick_class; + +void *joystick_new(t_float deltime, t_float scale, t_float translation); +void joystick_setup(void); +void joystick_read(t_joystick *x); +void joystick_change_deltime(t_joystick *x, t_float deltime); +void joystick_free(t_joystick *x); + +void *joystick_new(t_float deltime, t_float scale, t_float translation) +{ + int i, num_axes; + + t_joystick *x = (t_joystick *)pd_new(joystick_class); + + // open the joystick device read-only, non-exclusive + x->x_joy_fd = open (JOYSTICK_DEVICE, O_RDONLY | O_NONBLOCK); + + // read js_events from the JOYSTICK_DEVICE stream + while (read (x->x_joy_fd, &(x->x_joy_e), sizeof(struct js_event)) > -1); + + // if deltime is set in the object's creation arguments + // use that value, otherwise use the default + if (deltime == 0) + x->x_deltime = DEF_DELTIME; + else + x->x_deltime = (int)deltime; + + // if scale is set in the object's creation arguments + // use that value, otherwise use the default + if (scale == 0) + x->x_scale = DEF_SCALE; + else + x->x_scale = scale; + + // get translation from object arguments + x->x_translation = translation; + x->x_joy_axes = 0; + x->x_joy_buttons = 0; + + // get number of axes and buttons from device + ioctl (x->x_joy_fd, JSIOCGAXES, &(x->x_joy_axes)); + ioctl (x->x_joy_fd, JSIOCGBUTTONS, &(x->x_joy_buttons)); + + // this object handles a maximum of MAX_AXIS_OUTS axes + // if there are more, limit to MAX_AXIS_OUTS + if (x->x_joy_axes > MAX_AXIS_OUTS) + num_axes = MAX_AXIS_OUTS; + else + num_axes = x->x_joy_axes; + + // create outlets for each axis + for (i = 0; i < num_axes; i++) + x->x_axis_out[i] = outlet_new(&x->t_ob, &s_float); + + // create outlets for buttons + x->x_button_num_out = outlet_new(&x->t_ob, &s_float); + x->x_button_val_out = outlet_new(&x->t_ob, &s_float); + + + x->x_clock = clock_new (x, (t_method)joystick_read); + + clock_delay (x->x_clock, x->x_deltime); + + post ("configuring joystick:"); + post (" found %d axes", x->x_joy_axes); + post (" found %d buttons", x->x_joy_buttons); + return (void *)x; +} + +void joystick_setup(void) +{ + post ("joystick object loaded (J. Sarlo)"); + joystick_class = class_new(gensym("joystick"),(t_newmethod)joystick_new, + (t_method)joystick_free, sizeof(t_joystick), 0, A_DEFFLOAT, A_DEFFLOAT, + A_DEFFLOAT, 0); + class_addfloat(joystick_class, joystick_change_deltime); +} + +void joystick_read(t_joystick *x) +{ + int rt; + while (read (x->x_joy_fd, &(x->x_joy_e), sizeof(struct js_event)) > -1) + { + if (x->x_joy_e.type == JS_EVENT_AXIS) + { + outlet_float (x->x_axis_out[x->x_joy_e.number], + ((int)x->x_joy_e.value + x->x_translation) / x->x_scale); + } + else if (x->x_joy_e.type == JS_EVENT_BUTTON) + { + outlet_float (x->x_button_val_out, x->x_joy_e.value); + outlet_float (x->x_button_num_out, x->x_joy_e.number); + } + } + clock_delay (x->x_clock, x->x_deltime); +} + +void joystick_change_deltime(t_joystick *x, t_float deltime) +{ + if (deltime < 0) + deltime = 0; + x->x_deltime = deltime; +} + +void joystick_free(t_joystick *x) +{ + close (x->x_joy_fd); + clock_free (x->x_clock); +} diff --git a/rawmouse.c b/rawmouse.c new file mode 100644 index 0000000..0e1d2ae --- /dev/null +++ b/rawmouse.c @@ -0,0 +1,366 @@ +/* +rawmouse - an external object for Miller Puckette's Pure Data + +This object gets the raw data from the mouse for use in Pd +It is based on J. Sarlo's joystick + +Copyright (C) 2003 Hans-Christoph Steiner <hans@eds.org> + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "m_pd.h" + +#include <linux/input.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#define RAWMOUSE_DEVICE "/dev/input/event0" + +// scaling factor for output +#define RAWMOUSE_SCALE 1 +// delay/refresh time in milliseconds +#define RAWMOUSE_DELAYTIME 5 + +/* total supported number of axes and buttons */ +#define RAWMOUSE_AXES 3 +#define RAWMOUSE_BUTTONS 7 + +/* from <linux/input.h> + * supported buttons +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 + + * supported axes +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_WHEEL 0x08 +*/ + +/*------------------------------------------------------------------------------ + * from evtest.c from the ff-utils package + */ + +#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) + + +char *events[EV_MAX + 1] = { "Reset", "Key", "Relative", "Absolute", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, "LED", "Sound", NULL, "Repeat", "ForceFeedback", NULL, "ForceFeedbackStatus"}; +char *keys[KEY_MAX + 1] = { "Reserved", "Esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "Minus", "Equal", "Backspace", +"Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "LeftBrace", "RightBrace", "Enter", "LeftControl", "A", "S", "D", "F", "G", +"H", "J", "K", "L", "Semicolon", "Apostrophe", "Grave", "LeftShift", "BackSlash", "Z", "X", "C", "V", "B", "N", "M", "Comma", "Dot", +"Slash", "RightShift", "KPAsterisk", "LeftAlt", "Space", "CapsLock", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", +"NumLock", "ScrollLock", "KP7", "KP8", "KP9", "KPMinus", "KP4", "KP5", "KP6", "KPPlus", "KP1", "KP2", "KP3", "KP0", "KPDot", "103rd", +"F13", "102nd", "F11", "F12", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "KPEnter", "RightCtrl", "KPSlash", "SysRq", +"RightAlt", "LineFeed", "Home", "Up", "PageUp", "Left", "Right", "End", "Down", "PageDown", "Insert", "Delete", "Macro", "Mute", +"VolumeDown", "VolumeUp", "Power", "KPEqual", "KPPlusMinus", "Pause", "F21", "F22", "F23", "F24", "KPComma", "LeftMeta", "RightMeta", +"Compose", "Stop", "Again", "Props", "Undo", "Front", "Copy", "Open", "Paste", "Find", "Cut", "Help", "Menu", "Calc", "Setup", +"Sleep", "WakeUp", "File", "SendFile", "DeleteFile", "X-fer", "Prog1", "Prog2", "WWW", "MSDOS", "Coffee", "Direction", +"CycleWindows", "Mail", "Bookmarks", "Computer", "Back", "Forward", "CloseCD", "EjectCD", "EjectCloseCD", "NextSong", "PlayPause", +"PreviousSong", "StopCD", "Record", "Rewind", "Phone", "ISOKey", "Config", "HomePage", "Refresh", "Exit", "Move", "Edit", "ScrollUp", +"ScrollDown", "KPLeftParenthesis", "KPRightParenthesis", +"International1", "International2", "International3", "International4", "International5", +"International6", "International7", "International8", "International9", +"Language1", "Language2", "Language3", "Language4", "Language5", "Language6", "Language7", "Language8", "Language9", +NULL, +"PlayCD", "PauseCD", "Prog3", "Prog4", "Suspend", "Close", +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"Btn0", "Btn1", "Btn2", "Btn3", "Btn4", "Btn5", "Btn6", "Btn7", "Btn8", "Btn9", +NULL, NULL, NULL, NULL, NULL, NULL, +"LeftBtn", "RightBtn", "MiddleBtn", "SideBtn", "ExtraBtn", "ForwardBtn", "BackBtn", +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"Trigger", "ThumbBtn", "ThumbBtn2", "TopBtn", "TopBtn2", "PinkieBtn", +"BaseBtn", "BaseBtn2", "BaseBtn3", "BaseBtn4", "BaseBtn5", "BaseBtn6", +NULL, NULL, NULL, "BtnDead", +"BtnA", "BtnB", "BtnC", "BtnX", "BtnY", "BtnZ", "BtnTL", "BtnTR", "BtnTL2", "BtnTR2", "BtnSelect", "BtnStart", "BtnMode", +"BtnThumbL", "BtnThumbR", NULL, +"ToolPen", "ToolRubber", "ToolBrush", "ToolPencil", "ToolAirbrush", "ToolFinger", "ToolMouse", "ToolLens", NULL, NULL, +"Touch", "Stylus", "Stylus2" }; + +char *absval[5] = { "Value", "Min ", "Max ", "Fuzz ", "Flat " }; +char *relatives[REL_MAX + 1] = { "X", "Y", "Z", NULL, NULL, NULL, "HWheel", "Dial", "Wheel" }; +char *absolutes[ABS_MAX + 1] = { "X", "Y", "Z", "Rx", "Ry", "Rz", "Throttle", "Rudder", "Wheel", "Gas", "Brake", +NULL, NULL, NULL, NULL, NULL, +"Hat0X", "Hat0Y", "Hat1X", "Hat1Y", "Hat2X", "Hat2Y", "Hat3X", "Hat 3Y", "Pressure", "Distance", "XTilt", "YTilt"}; +char *leds[LED_MAX + 1] = { "NumLock", "CapsLock", "ScrollLock", "Compose", "Kana", "Sleep", "Suspend", "Mute" }; +char *repeats[REP_MAX + 1] = { "Delay", "Period" }; +char *sounds[SND_MAX + 1] = { "Bell", "Click" }; + +char **names[EV_MAX + 1] = { events, keys, relatives, absolutes, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, leds, sounds, NULL, repeats, NULL, NULL, NULL }; + +/*------------------------------------------------------------------------------ + */ + + +/* + * The event structure itself from <linux/input.h> + * only here as a reference + +struct input_event { + struct timeval time; + unsigned short type; + unsigned short code; + unsigned int value; +}; +*/ + +/*------------------------------------------------------------------------------ + * CLASS DEF + */ + +typedef struct _rawmouse { + t_object t_ob; + struct input_event x_mouse_event[64]; /* the events (up to 64 at once) */ + int x_mouse_fd; + float x_scale; + float x_translation; + t_outlet *x_axis_out[RAWMOUSE_AXES]; + t_outlet *x_button_num_out; + t_outlet *x_button_val_out; + t_clock *x_clock; + double x_delaytime; + unsigned char x_mouse_buttons; + unsigned char x_mouse_axes; +}t_rawmouse; + +t_class *rawmouse_class; + + +/*------------------------------------------------------------------------------ + * INTERFACE + */ + +void *rawmouse_new(t_float delaytime, t_float scale, t_float translation); +void rawmouse_setup(void); +void rawmouse_read(t_rawmouse *x); +void rawmouse_change_delaytime(t_rawmouse *x, t_float delaytime); +void rawmouse_free(t_rawmouse *x); + + +/*------------------------------------------------------------------------------ + * IMPLEMENTATION + */ + +void *rawmouse_new(t_float delaytime, t_float scale, t_float translation) +{ + int i,eventtype, eventcode, num_axes; + unsigned long bitmask[EV_MAX][NBITS(KEY_MAX)]; + char devicename[256] = "Unknown"; + + + t_rawmouse *x = (t_rawmouse *)pd_new(rawmouse_class); + + /* open the rawmouse device read-only, non-exclusive */ + x->x_mouse_fd = open (RAWMOUSE_DEVICE, O_RDONLY | O_NONBLOCK); + + /* read input_events from the RAWMOUSE_DEVICE stream + * + * what is this doing? its doesn't make sense + * there seems to be two while loops in joystick.c reading + * the input device, one in _new, and the other in _read + while (read (x->x_mouse_fd, &(x->x_mouse_event), sizeof(struct input_event)) > -1); + */ + while ( read(x->x_mouse_fd, &(x->x_mouse_event), sizeof(struct input_event) * 64) > -1); + + /* if delaytime is set in the object's creation arguments + * use that value, otherwise use the default */ + if (delaytime == 0) + x->x_delaytime = RAWMOUSE_DELAYTIME; + else + x->x_delaytime = (int)delaytime; + + /* if scale is set in the object's creation arguments + * use that value, otherwise use the default */ + if (scale == 0) + x->x_scale = RAWMOUSE_SCALE; + else + x->x_scale = scale; + + /* get translation from object arguments */ + x->x_translation = translation; + + /* from evtest.c in the ff-utils distro + * get the number of supported axes and buttons + * EVIOCGBIT (EV_KEY) for buttons + * EVIOCGBIT (EV_REL) for axes + */ + + /* get name of device */ + ioctl(x->x_mouse_fd, EVIOCGNAME(sizeof(devicename)), devicename); + post ("configuring %s",devicename); + + /* get bitmask representing supported events (axes, buttons, etc.) */ + memset(bitmask, 0, sizeof(bitmask)); + ioctl(x->x_mouse_fd, EVIOCGBIT(0, EV_MAX), bitmask[0]); + post("Supported events:"); + + x->x_mouse_axes = 0; + x->x_mouse_buttons = 0; + + /* cycle through all possible event types */ + for (eventtype = 0; eventtype < EV_MAX; eventtype++) { + if (test_bit(eventtype, bitmask[0])) { + post(" %s (type %d) ", events[eventtype] ? events[eventtype] : "?", eventtype); + // post("Event type %d",eventtype); + + /* get bitmask representing supported button types */ + ioctl(x->x_mouse_fd, EVIOCGBIT(eventtype, KEY_MAX), bitmask[eventtype]); + + /* cycle through all possible event codes (axes, keys, etc.) + * testing to see which are supported + */ + for (eventcode = 0; eventcode < KEY_MAX; eventcode++) + if (test_bit(eventcode, bitmask[eventtype])) { + post(" Event code %d (%s)", eventcode, names[eventtype] ? (names[eventtype][eventcode] ? names[eventtype][eventcode] : "?") : "?"); + // post (" Event code %d", eventcode); + x->x_mouse_buttons += 1; + post(" number: %d eventtype: %d eventcode: %d",x->x_mouse_buttons, eventtype, eventcode); + } + } + } + + /* create outlets for each axis */ + for (i = 0; i < RAWMOUSE_AXES; i++) + x->x_axis_out[i] = outlet_new(&x->t_ob, &s_float); + + /* create outlets for buttons */ + x->x_button_num_out = outlet_new(&x->t_ob, &s_float); + x->x_button_val_out = outlet_new(&x->t_ob, &s_float); + + /* get pd clock */ + x->x_clock = clock_new (x, (t_method)rawmouse_read); + + /* set refresh time */ + clock_delay (x->x_clock, x->x_delaytime); + + post (" found %d axes", x->x_mouse_axes); + post (" found %d buttons", x->x_mouse_buttons); + + return (void *)x; +} + +void rawmouse_setup(void) +{ + post ("rawmouse object loaded using %s",RAWMOUSE_DEVICE); + rawmouse_class = class_new(gensym("rawmouse"),(t_newmethod)rawmouse_new, + (t_method)rawmouse_free, sizeof(t_rawmouse), 0, A_DEFFLOAT, A_DEFFLOAT, + A_DEFFLOAT, 0); + class_addfloat(rawmouse_class, rawmouse_change_delaytime); +} + +void rawmouse_read(t_rawmouse *x) +{ + /* Currently, the mouse button #s are directly taken from the + * sequence in the Linux input event system <linux/input.h> + * this might have to change when creating the MacOS X and + * Windows objects in order to keep it consistent across + * all platforms. Let's hope the order is the same on all... + */ + int i; /* loop counter */ + int axis_num; + t_float button_num; + size_t read_bytes; /* how many bytes were read */ + + while (1) { + read_bytes = read(x->x_mouse_fd, &(x->x_mouse_event), sizeof(struct input_event) * 64); + + if (read_bytes < (int) sizeof(struct input_event)) { + post("rawmouse: short read"); + break; + } + + for (i = 0; i < (int) (read_bytes / sizeof(struct input_event)); i++) { + post("Event: time %ld.%06ld, type %d, code %d, value %d", + x->x_mouse_event[i].time.tv_sec, x->x_mouse_event[i].time.tv_usec, + x->x_mouse_event[i].type,x->x_mouse_event[i].code, x->x_mouse_event[i].value); + + if ( x->x_mouse_event[i].type == EV_KEY ) { + /* key/button event type */ + switch ( x->x_mouse_event[i].code ) { + case BTN_LEFT: + button_num = 0; + break; + case BTN_RIGHT: + button_num = 1; + break; + case BTN_MIDDLE: + button_num = 2; + break; + case BTN_SIDE: + button_num = 3; + break; + case BTN_EXTRA: + button_num = 4; + break; + case BTN_FORWARD: + button_num = 5; + break; + case BTN_BACK: + button_num = 6; + break; + } + outlet_float (x->x_button_val_out, x->x_mouse_event[i].value); + outlet_float (x->x_button_num_out, button_num); + } + else if ( x->x_mouse_event[i].type == EV_REL ) { + /* Relative Axes Event Type */ + switch ( x->x_mouse_event[i].code ) { + case REL_X: + axis_num = 0; + break; + case REL_Y: + axis_num = 1; + break; + case REL_WHEEL: + axis_num = 2; + break; + } + outlet_float (x->x_axis_out[axis_num], x->x_mouse_event[i].value); + } + } + } + clock_delay (x->x_clock, x->x_delaytime); +} + +void rawmouse_change_delaytime(t_rawmouse *x, t_float delaytime) +{ + if (delaytime < 0) + delaytime = 0; + x->x_delaytime = delaytime; +} + +void rawmouse_free(t_rawmouse *x) +{ + close (x->x_mouse_fd); + clock_free (x->x_clock); +} |