Fixed comment, added COPYING
[tmd.git] / src / tmd.c
blobc29479af4d3afd91abf0f743a12db75570739b2b
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <X11/Xlib.h>
7 #include <X11/extensions/XInput2.h>
9 #define VERSION "0.1"
10 #define MD_PREFIX "TUIO "
11 #define PDEBUG(msg, ...) if(debug) printf("TMD: "msg,##__VA_ARGS__);
13 typedef struct _attach_info {
14 struct _attach_info *next;
16 int slaveid;
17 int masterid;
18 char *m_name;
19 } AttachInfo, *AttachInfoPtr;
21 /* Attach Info "root" node */
22 static AttachInfo ais;
24 static int debug = 0;
25 static int foreground = 1;
27 /**
28 * Removes a master device.
30 int remove_master(Display* dpy, int master_id) {
31 XIRemoveMasterInfo c;
33 c.type = XIRemoveMaster;
34 c.deviceid = master_id;
35 c.return_mode = XIFloating;
37 return XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1);
40 /**
41 * Creates a new master device.
43 int create_master(Display* dpy, char* name) {
44 XIAddMasterInfo c;
46 c.type = XIAddMaster;
47 c.name = name;
48 c.send_core = 1;
49 c.enable = 1;
51 return XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1);
54 /**
55 * Attaches a slave device to a master device.
57 int change_attachment(Display* dpy, int slaveid, int masterid) {
58 XIAttachSlaveInfo c;
60 c.type = XIAttachSlave;
61 c.deviceid = slaveid;
62 c.new_master = masterid;
64 return XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1);
67 /**
68 * Listens for the creation of slave and master devices. When a new slave
69 * device is found, a new master device will be created and attached to
70 * when the message for the new master device comes back around.
72 void listen(Display *dpy, int xi_opcode) {
73 int num_devices;
74 unsigned char hmask[2] = { 0, 0 };
75 char *m_name, *s_name;
76 XEvent ev;
77 XIDeviceInfo *info;
78 XIEventMask evmask_h;
79 AttachInfoPtr ai;
81 //Cursor m_cur;
82 //m_cur = XCreateFontCursor(dpy, 88);
83 //if (m_cur == BadAlloc || m_cur == BadValue) {
84 //printf("Unable to create cursor\n");
85 //}
87 evmask_h.mask = hmask;
88 evmask_h.mask_len = sizeof(hmask);
89 evmask_h.deviceid = XIAllDevices;
90 XISetMask(hmask, XI_HierarchyChanged);
91 XFlush(dpy);
93 XISelectEvents(dpy, DefaultRootWindow(dpy), &evmask_h, 1);
95 PDEBUG("Listening for events...\n");
97 /* Start listening for events... */
98 while (1) {
99 XGenericEventCookie *cookie;
100 cookie = &ev.xcookie;
101 XNextEvent(dpy, &ev);
103 if (!XGetEventData(dpy, &ev))
104 continue;
106 PDEBUG("Event Received\n");
108 if (cookie->type != GenericEvent ||
109 cookie->extension != xi_opcode)
110 continue;
112 if (cookie->evtype == XI_HierarchyChanged) {
113 int i;
114 XIHierarchyEvent *event = cookie->data;
116 if (event->flags & XISlaveAdded) {
117 PDEBUG(" Slave added\n");
119 for (i=0; i < event->num_info; i++) {
120 if (event->info[i].flags & XISlaveAdded) {
121 info = XIQueryDevice(dpy, event->info[i].deviceid, &num_devices);
122 if (num_devices != 1) {
123 PDEBUG(" Couldn't find slave device (XIQueryDevice)\n");
124 XIFreeDeviceInfo(info);
125 continue;
127 s_name = info->name;
129 PDEBUG(" Found: %s\n", s_name);
131 /* This is a hack. XTEST slave devices are created and
132 * attached to each new master device within X. If we
133 * were to actually create a new master device for this
134 * device we would end up with a recurring creation of
135 * master devices and slave devices. Also, tuio subdevices
136 * will have the word "subdev" in them. */
137 if (strstr(s_name, "XTEST") ||
138 !strstr(s_name, "subdev")) {
139 PDEBUG(" Device is NOT a tuio subdevice\n");
140 XIFreeDeviceInfo(info);
141 continue;
142 } else {
143 PDEBUG(" Device is a tuio subdevice, "
144 "creating Master Device\n");
147 asprintf(&m_name, MD_PREFIX "%s", s_name);
148 create_master(dpy, m_name);
149 free(m_name);
151 ai = malloc(sizeof(AttachInfo));
152 ai->next = ais.next;
153 ais.next = ai;
155 /* The new pointer will have the suffix " pointer" */
156 asprintf(&ai->m_name, MD_PREFIX "%s pointer", s_name);
157 ai->slaveid = event->info[i].deviceid;
158 XIFreeDeviceInfo(info);
163 if (event->flags & XIMasterAdded) {
164 PDEBUG(" Master added\n");
166 for (i=0; i < event->num_info; i++) {
167 if(event->info[i].flags & XIMasterAdded) {
169 info = XIQueryDevice(dpy, event->info[i].deviceid, &num_devices);
170 if (num_devices != 1) {
171 PDEBUG(" Couldn't find master device (XIQueryDevice)\n");
172 XIFreeDeviceInfo(info);
173 continue;
175 m_name = info->name;
177 PDEBUG(" Found: %s, reattaching\n", m_name);
179 ai = ais.next;
180 while (ai != NULL) {
181 if (strcmp(ai->m_name, m_name) == 0) {
182 PDEBUG(" New master found in internal list\n");
183 change_attachment(dpy, ai->slaveid, event->info[i].deviceid);
184 ai->masterid = event->info[i].deviceid;
185 //XIDefineCursor(dpy, event->info[i].deviceid,
186 //DefaultRootWindow(dpy), m_cur);
188 ai = ai->next;
190 XIFreeDeviceInfo(info);
195 if (event->flags & XISlaveRemoved) {
196 PDEBUG(" Slave removed\n");
197 for (i=0; i < event->num_info; i++) {
198 if(event->info[i].flags & XISlaveRemoved) {
199 int m_id;
200 AttachInfo *ai_last;
202 ai_last = &ais;
203 ai = ais.next;
204 while (ai != NULL) {
205 if (ai->slaveid == event->info[i].deviceid) {
207 /* Remove corresponding master device */
208 m_id = ai->masterid;
209 PDEBUG(" Removing Master: %d\n", m_id);
210 remove_master(dpy, m_id);
212 ai_last->next = ai->next;
214 free(ai->m_name);
215 free(ai);
217 ai_last = ai;
218 ai = ai->next;
224 XFreeEventData(dpy, &ev);
229 * Forks the process sets
231 int daemonize() {
232 int pid;
234 pid = fork();
235 if (pid < 0) {
236 printf("Failed to fork\n");
237 return -1;
238 } else if (pid > 0) {
239 exit(0);
242 umask(0);
244 if ((chdir("/")) < 0) {
245 return -1;
248 freopen( "/dev/null", "r", stdin);
249 freopen( "/dev/null", "w", stdout);
250 freopen( "/dev/null", "w", stderr);
254 * Prints help information.
256 void printHelp(char* bin_name) {
257 printf("Usage: %s [OPTION...]\n\n", bin_name);
258 printf(" -d\tenables debugging output\n");
259 printf(" -f\truns process in foreground\n");
260 printf(" -h\tprints this help message\n");
264 * Processes application arguments.
266 void processArgs(int argc, char **argv) {
267 int i;
269 for (i=1; i<argc; i++) {
270 if (strcmp(argv[i], "-d") == 0) {
271 debug = 1;
272 PDEBUG("Debug on\n");
273 } else if (strcmp(argv[i], "-f") == 0) {
274 foreground = 1;
275 } else if (strcmp(argv[i], "-h") == 0) {
276 printHelp(argv[0]);
277 exit(0);
278 } else if (strcmp(argv[i], "-v") == 0) {
279 printf("Version "VERSION"\n");
280 exit(0);
285 int main (int argc, char **argv) {
286 Display *dpy;
287 int xi_opcode, event, error;
288 int major, minor;
289 int ret;
291 ais.next = NULL;
293 processArgs(argc, argv);
294 if (!foreground)
295 daemonize();
297 PDEBUG("Connecting to X\n");
298 /* Open X display */
299 dpy = XOpenDisplay(NULL);
300 if (!dpy) {
301 printf("Failed to open display.\n");
302 return -1;
305 /* Make sure the X Input extension is available */
306 if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) {
307 printf("X Input extension not available.\nExiting\n");
308 return -1;
311 /* Check XI version, need 2.0 */
312 major = 2;
313 minor = 0;
314 ret = XIQueryVersion(dpy, &major, &minor);
315 if (ret == BadRequest) {
316 printf("XInput 2.0 not supported. Server supports %i.%i.\nExiting\n", major, minor);
317 return -1;
320 listen(dpy, xi_opcode);
322 XCloseDisplay(dpy);
323 return 0;