1/*
2 * Copyright © 2009 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25
26#include "xinput.h"
27#include <string.h>
28
29extern void print_classes_xi2(Display*, XIAnyClassInfo **classes,
30                              int num_classes);
31
32static Window create_win(Display *dpy)
33{
34    Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 200,
35            200, 0, 0, WhitePixel(dpy, 0));
36    Window subwindow = XCreateSimpleWindow(dpy, win, 50, 50, 50, 50, 0, 0,
37            BlackPixel(dpy, 0));
38
39    XMapWindow(dpy, subwindow);
40    XSelectInput(dpy, win, ExposureMask);
41    return win;
42}
43
44static void print_deviceevent(XIDeviceEvent* event)
45{
46    double *val;
47    int i;
48
49    printf("    device: %d (%d)\n", event->deviceid, event->sourceid);
50    printf("    time: %ld\n", event->time);
51    printf("    detail: %d\n", event->detail);
52    switch(event->evtype) {
53        case XI_KeyPress:
54        case XI_KeyRelease:
55            printf("    flags: %s\n", (event->flags & XIKeyRepeat) ?  "repeat" : "");
56            break;
57#if HAVE_XI21
58        case XI_ButtonPress:
59        case XI_ButtonRelease:
60        case XI_Motion:
61            printf("    flags: %s\n", (event->flags & XIPointerEmulated) ?  "emulated" : "");
62            break;
63#endif
64#if HAVE_XI22
65        case XI_TouchBegin:
66        case XI_TouchUpdate:
67        case XI_TouchEnd:
68            printf("    flags:%s%s\n",
69                   (event->flags & XITouchPendingEnd) ?  " pending_end" : "",
70                   (event->flags & XITouchEmulatingPointer) ?  " emulating" : "");
71            break;
72#endif
73    }
74
75    printf("    root: %.2f/%.2f\n", event->root_x, event->root_y);
76    printf("    event: %.2f/%.2f\n", event->event_x, event->event_y);
77
78    printf("    buttons:");
79    for (i = 0; i < event->buttons.mask_len * 8; i++)
80        if (XIMaskIsSet(event->buttons.mask, i))
81            printf(" %d", i);
82    printf("\n");
83
84    printf("    modifiers: locked %#x latched %#x base %#x effective: %#x\n",
85            event->mods.locked, event->mods.latched,
86            event->mods.base, event->mods.effective);
87    printf("    group: locked %#x latched %#x base %#x effective: %#x\n",
88            event->group.locked, event->group.latched,
89            event->group.base, event->group.effective);
90    printf("    valuators:\n");
91
92    val = event->valuators.values;
93    for (i = 0; i < event->valuators.mask_len * 8; i++)
94        if (XIMaskIsSet(event->valuators.mask, i))
95            printf("        %i: %.2f\n", i, *val++);
96
97    printf("    windows: root 0x%lx event 0x%lx child 0x%lx\n",
98            event->root, event->event, event->child);
99}
100
101static void print_devicechangedevent(Display *dpy, XIDeviceChangedEvent *event)
102{
103    printf("    device: %d (%d)\n", event->deviceid, event->sourceid);
104    printf("    time: %ld\n", event->time);
105    printf("    reason: %s\n", (event->reason == XISlaveSwitch) ? "SlaveSwitch" :
106                                "DeviceChanged");
107    print_classes_xi2(dpy, event->classes, event->num_classes);
108}
109
110static void print_hierarchychangedevent(XIHierarchyEvent *event)
111{
112    int i;
113    printf("    time: %ld\n", event->time);
114    printf("    Changes happened: %s %s %s %s %s %s %s %s\n",
115            (event->flags & XIMasterAdded) ? "[new master]" : "",
116            (event->flags & XIMasterRemoved) ? "[master removed]" : "",
117            (event->flags & XISlaveAdded) ? "[new slave]" : "",
118            (event->flags & XISlaveRemoved) ? "[slave removed]" : "",
119            (event->flags & XISlaveAttached) ? "[slave attached]" : "",
120            (event->flags & XISlaveDetached) ? "[slave detached]" : "",
121            (event->flags & XIDeviceEnabled) ? "[device enabled]" : "",
122            (event->flags & XIDeviceDisabled) ? "[device disabled]" : "");
123
124    for (i = 0; i < event->num_info; i++)
125    {
126        char *use = "<undefined>";
127        switch(event->info[i].use)
128        {
129            case XIMasterPointer: use = "master pointer"; break;
130            case XIMasterKeyboard: use = "master keyboard"; break;
131            case XISlavePointer: use = "slave pointer"; break;
132            case XISlaveKeyboard: use = "slave keyboard"; break;
133            case XIFloatingSlave: use = "floating slave"; break;
134                break;
135        }
136
137        printf("    device %d [%s (%d)] is %s\n",
138                event->info[i].deviceid,
139                use,
140                event->info[i].attachment,
141                (event->info[i].enabled) ? "enabled" : "disabled");
142        if (event->info[i].flags)
143        {
144            printf("    changes: %s %s %s %s %s %s %s %s\n",
145                    (event->info[i].flags & XIMasterAdded) ? "[new master]" : "",
146                    (event->info[i].flags & XIMasterRemoved) ? "[master removed]" : "",
147                    (event->info[i].flags & XISlaveAdded) ? "[new slave]" : "",
148                    (event->info[i].flags & XISlaveRemoved) ? "[slave removed]" : "",
149                    (event->info[i].flags & XISlaveAttached) ? "[slave attached]" : "",
150                    (event->info[i].flags & XISlaveDetached) ? "[slave detached]" : "",
151                    (event->info[i].flags & XIDeviceEnabled) ? "[device enabled]" : "",
152                    (event->info[i].flags & XIDeviceDisabled) ? "[device disabled]" : "");
153        }
154    }
155}
156
157static void print_rawevent(XIRawEvent *event)
158{
159    int i;
160    double *val, *raw_val;
161
162    printf("    device: %d (%d)\n", event->deviceid, event->sourceid);
163    printf("    time:   %ld\n", event->time);
164    printf("    detail: %d\n", event->detail);
165#if HAVE_XI21
166    switch(event->evtype) {
167        case XI_RawButtonPress:
168        case XI_RawButtonRelease:
169        case XI_RawMotion:
170            printf("    flags: %s\n", (event->flags & XIPointerEmulated) ?  "emulated" : "");
171            break;
172    }
173#endif
174
175    printf("    valuators:\n");
176    val = event->valuators.values;
177    raw_val = event->raw_values;
178    for (i = 0; i < event->valuators.mask_len * 8; i++)
179        if (XIMaskIsSet(event->valuators.mask, i))
180            printf("         %2d: %.2f (%.2f)\n", i, *val++, *raw_val++);
181    printf("\n");
182}
183
184static void print_enterleave(XILeaveEvent* event)
185{
186    char *mode = "<undefined>",
187         *detail = "<undefined>";
188    int i;
189
190    printf("    device: %d (%d)\n", event->deviceid, event->sourceid);
191    printf("    time:   %ld\n", event->time);
192    printf("    windows: root 0x%lx event 0x%lx child 0x%ld\n",
193            event->root, event->event, event->child);
194    switch(event->mode)
195    {
196        case XINotifyNormal:       mode = "NotifyNormal"; break;
197        case XINotifyGrab:         mode = "NotifyGrab"; break;
198        case XINotifyUngrab:       mode = "NotifyUngrab"; break;
199        case XINotifyWhileGrabbed: mode = "NotifyWhileGrabbed"; break;
200        case XINotifyPassiveGrab:  mode = "NotifyPassiveGrab"; break;
201        case XINotifyPassiveUngrab:mode = "NotifyPassiveUngrab"; break;
202    }
203    switch (event->detail)
204    {
205        case XINotifyAncestor: detail = "NotifyAncestor"; break;
206        case XINotifyVirtual: detail = "NotifyVirtual"; break;
207        case XINotifyInferior: detail = "NotifyInferior"; break;
208        case XINotifyNonlinear: detail = "NotifyNonlinear"; break;
209        case XINotifyNonlinearVirtual: detail = "NotifyNonlinearVirtual"; break;
210        case XINotifyPointer: detail = "NotifyPointer"; break;
211        case XINotifyPointerRoot: detail = "NotifyPointerRoot"; break;
212        case XINotifyDetailNone: detail = "NotifyDetailNone"; break;
213    }
214    printf("    mode: %s (detail %s)\n", mode, detail);
215    printf("    flags: %s %s\n", event->focus ? "[focus]" : "",
216                                 event->same_screen ? "[same screen]" : "");
217    printf("    buttons:");
218    for (i = 0; i < event->buttons.mask_len * 8; i++)
219        if (XIMaskIsSet(event->buttons.mask, i))
220            printf(" %d", i);
221    printf("\n");
222
223    printf("    modifiers: locked %#x latched %#x base %#x effective: %#x\n",
224            event->mods.locked, event->mods.latched,
225            event->mods.base, event->mods.effective);
226    printf("    group: locked %#x latched %#x base %#x effective: %#x\n",
227            event->group.locked, event->group.latched,
228            event->group.base, event->group.effective);
229
230    printf("    root x/y:  %.2f / %.2f\n", event->root_x, event->root_y);
231    printf("    event x/y: %.2f / %.2f\n", event->event_x, event->event_y);
232
233}
234
235static void print_propertyevent(Display *display, XIPropertyEvent* event)
236{
237    char *changed;
238    char *name;
239
240    if (event->what == XIPropertyDeleted)
241        changed = "deleted";
242    else if (event->what == XIPropertyCreated)
243        changed = "created";
244    else
245        changed = "modified";
246    name = XGetAtomName(display, event->property);
247    printf("     time:   %ld\n", event->time);
248    printf("     property: %ld '%s'\n", event->property, name);
249    printf("     changed: %s\n", changed);
250
251    XFree(name);
252}
253void
254test_sync_grab(Display *display, Window win)
255{
256    int loop = 3;
257    int rc;
258    XIEventMask mask;
259
260    /* Select for motion events */
261    mask.deviceid = XIAllDevices;
262    mask.mask_len = 2;
263    mask.mask = calloc(2, sizeof(char));
264    XISetMask(mask.mask, XI_ButtonPress);
265
266    if ((rc = XIGrabDevice(display, 2,  win, CurrentTime, None, GrabModeSync,
267                           GrabModeAsync, False, &mask)) != GrabSuccess)
268    {
269        fprintf(stderr, "Grab failed with %d\n", rc);
270        return;
271    }
272    free(mask.mask);
273
274    XSync(display, True);
275    XIAllowEvents(display, 2, SyncPointer, CurrentTime);
276    XFlush(display);
277
278    printf("Holding sync grab for %d button presses.\n", loop);
279
280    while(loop--)
281    {
282        XIEvent ev;
283
284        XNextEvent(display, (XEvent*)&ev);
285        if (ev.type == GenericEvent && ev.extension == xi_opcode )
286        {
287            XIDeviceEvent *event = (XIDeviceEvent*)&ev;
288            print_deviceevent(event);
289            XIAllowEvents(display, 2, SyncPointer, CurrentTime);
290        }
291    }
292
293    XIUngrabDevice(display, 2, CurrentTime);
294    printf("Done\n");
295}
296
297static const char* type_to_name(int evtype)
298{
299    const char *name;
300
301    switch(evtype) {
302        case XI_DeviceChanged:    name = "DeviceChanged";       break;
303        case XI_KeyPress:         name = "KeyPress";            break;
304        case XI_KeyRelease:       name = "KeyRelease";          break;
305        case XI_ButtonPress:      name = "ButtonPress";         break;
306        case XI_ButtonRelease:    name = "ButtonRelease";       break;
307        case XI_Motion:           name = "Motion";              break;
308        case XI_Enter:            name = "Enter";               break;
309        case XI_Leave:            name = "Leave";               break;
310        case XI_FocusIn:          name = "FocusIn";             break;
311        case XI_FocusOut:         name = "FocusOut";            break;
312        case XI_HierarchyChanged: name = "HierarchyChanged";    break;
313        case XI_PropertyEvent:    name = "PropertyEvent";       break;
314        case XI_RawKeyPress:      name = "RawKeyPress";         break;
315        case XI_RawKeyRelease:    name = "RawKeyRelease";       break;
316        case XI_RawButtonPress:   name = "RawButtonPress";      break;
317        case XI_RawButtonRelease: name = "RawButtonRelease";    break;
318        case XI_RawMotion:        name = "RawMotion";           break;
319        case XI_TouchBegin:       name = "TouchBegin";          break;
320        case XI_TouchUpdate:      name = "TouchUpdate";         break;
321        case XI_TouchEnd:         name = "TouchEnd";            break;
322        case XI_RawTouchBegin:    name = "RawTouchBegin";       break;
323        case XI_RawTouchUpdate:   name = "RawTouchUpdate";      break;
324        case XI_RawTouchEnd:      name = "RawTouchEnd";         break;
325        default:
326                                  name = "unknown event type"; break;
327    }
328    return name;
329}
330
331
332int
333test_xi2(Display	*display,
334         int	argc,
335         char	*argv[],
336         char	*name,
337         char	*desc)
338{
339    XIEventMask mask[2];
340    XIEventMask *m;
341    Window win;
342    int deviceid = -1;
343    int use_root = 0;
344    int rc;
345
346    setvbuf(stdout, NULL, _IOLBF, 0);
347
348    if (argc >= 1 && strcmp(argv[0], "--root") == 0) {
349        use_root = 1;
350
351        argc--;
352        argv++;
353    }
354
355    rc = list(display, argc, argv, name, desc);
356    if (rc != EXIT_SUCCESS)
357        return rc;
358
359    if (use_root)
360        win = DefaultRootWindow(display);
361    else
362        win = create_win(display);
363
364    if (argc >= 1) {
365        XIDeviceInfo *info;
366        info = xi2_find_device_info(display, argv[0]);
367        /* info is alway valid, the list() call exits if the device
368           cannot be found, but let's shut up coverity */
369        if (!info)
370            return EXIT_FAILURE;
371        deviceid = info->deviceid;
372    }
373
374    /* Select for motion events */
375    m = &mask[0];
376    m->deviceid = (deviceid == -1) ? XIAllDevices : deviceid;
377    m->mask_len = XIMaskLen(XI_LASTEVENT);
378    m->mask = calloc(m->mask_len, sizeof(char));
379    XISetMask(m->mask, XI_ButtonPress);
380    XISetMask(m->mask, XI_ButtonRelease);
381    XISetMask(m->mask, XI_KeyPress);
382    XISetMask(m->mask, XI_KeyRelease);
383    XISetMask(m->mask, XI_Motion);
384    XISetMask(m->mask, XI_DeviceChanged);
385    XISetMask(m->mask, XI_Enter);
386    XISetMask(m->mask, XI_Leave);
387    XISetMask(m->mask, XI_FocusIn);
388    XISetMask(m->mask, XI_FocusOut);
389#if HAVE_XI22
390    XISetMask(m->mask, XI_TouchBegin);
391    XISetMask(m->mask, XI_TouchUpdate);
392    XISetMask(m->mask, XI_TouchEnd);
393#endif
394    if (m->deviceid == XIAllDevices)
395        XISetMask(m->mask, XI_HierarchyChanged);
396    XISetMask(m->mask, XI_PropertyEvent);
397
398    m = &mask[1];
399    m->deviceid = (deviceid == -1) ? XIAllMasterDevices : deviceid;
400    m->mask_len = XIMaskLen(XI_LASTEVENT);
401    m->mask = calloc(m->mask_len, sizeof(char));
402    XISetMask(m->mask, XI_RawKeyPress);
403    XISetMask(m->mask, XI_RawKeyRelease);
404    XISetMask(m->mask, XI_RawButtonPress);
405    XISetMask(m->mask, XI_RawButtonRelease);
406    XISetMask(m->mask, XI_RawMotion);
407#if HAVE_XI22
408    XISetMask(m->mask, XI_RawTouchBegin);
409    XISetMask(m->mask, XI_RawTouchUpdate);
410    XISetMask(m->mask, XI_RawTouchEnd);
411#endif
412
413    XISelectEvents(display, win, &mask[0], use_root ? 2 : 1);
414    if (!use_root) {
415        XISelectEvents(display, DefaultRootWindow(display), &mask[1], 1);
416        XMapWindow(display, win);
417    }
418    XSync(display, False);
419
420    free(mask[0].mask);
421    free(mask[1].mask);
422
423    if (!use_root) {
424        XEvent event;
425        XMaskEvent(display, ExposureMask, &event);
426        XSelectInput(display, win, 0);
427    }
428
429    /*
430    test_sync_grab(display, win);
431    */
432
433    while(1)
434    {
435        XEvent ev;
436        XGenericEventCookie *cookie = (XGenericEventCookie*)&ev.xcookie;
437        XNextEvent(display, (XEvent*)&ev);
438
439        if (XGetEventData(display, cookie) &&
440            cookie->type == GenericEvent &&
441            cookie->extension == xi_opcode)
442        {
443            printf("EVENT type %d (%s)\n", cookie->evtype, type_to_name(cookie->evtype));
444            switch (cookie->evtype)
445            {
446                case XI_DeviceChanged:
447                    print_devicechangedevent(display, cookie->data);
448                    break;
449                case XI_HierarchyChanged:
450                    print_hierarchychangedevent(cookie->data);
451                    break;
452                case XI_RawKeyPress:
453                case XI_RawKeyRelease:
454                case XI_RawButtonPress:
455                case XI_RawButtonRelease:
456                case XI_RawMotion:
457                case XI_RawTouchBegin:
458                case XI_RawTouchUpdate:
459                case XI_RawTouchEnd:
460                    print_rawevent(cookie->data);
461                    break;
462                case XI_Enter:
463                case XI_Leave:
464                case XI_FocusIn:
465                case XI_FocusOut:
466                    print_enterleave(cookie->data);
467                    break;
468                case XI_PropertyEvent:
469                    print_propertyevent(display, cookie->data);
470                    break;
471                default:
472                    print_deviceevent(cookie->data);
473                    break;
474            }
475        }
476
477        XFreeEventData(display, cookie);
478    }
479
480    XDestroyWindow(display, win);
481
482    return EXIT_SUCCESS;
483}
484