/* test-xinput.c --- playing with the XInput2 extension.
* xscreensaver, Copyright © 2021 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/Xproto.h>
#include <X11/extensions/XInput2.h>
#include "blurb.h"
#include "xinput.h"
char *progclass = "XScreenSaver";
Bool debug_p = True;
static void
ungrab_timer (XtPointer closure, XtIntervalId *id)
{
Display *dpy = (Display *) closure;
fprintf (stderr, "\n%s: ungrabbing\n\n", blurb());
XUngrabKeyboard (dpy, CurrentTime);
XUngrabPointer (dpy, CurrentTime);
}
static const char *
grab_string (int status)
{
switch (status) {
case GrabSuccess: return "GrabSuccess";
case AlreadyGrabbed: return "AlreadyGrabbed";
case GrabInvalidTime: return "GrabInvalidTime";
case GrabNotViewable: return "GrabNotViewable";
case GrabFrozen: return "GrabFrozen";
default:
{
static char buf[255];
sprintf(buf, "unknown status: %d", status);
return buf;
}
}
}
int
main (int argc, char **argv)
{
XtAppContext app;
Widget toplevel_shell;
Display *dpy;
int xi_opcode;
Bool grab_kbd_p = False;
Bool grab_mouse_p = False;
Bool mouse_sync_p = True;
Bool kbd_sync_p = True;
int i;
progname = argv[0];
for (i = 1; i < argc; i++)
{
const char *oa = argv[i];
if (argv[i][0] == '-' && argv[i][1] == '-')
argv[i]++;
if (!strcmp (argv[i], "-grab"))
grab_kbd_p = grab_mouse_p = True;
else if (!strcmp (argv[i], "-grab-kbd") ||
!strcmp (argv[i], "-grab-keyboard"))
grab_kbd_p = True;
else if (!strcmp (argv[i], "-grab-mouse") ||
!strcmp (argv[i], "-grab-pointer"))
grab_mouse_p = True;
else if (!strcmp (argv[i], "-kbd-sync") ||
!strcmp (argv[i], "-keyboard-sync"))
kbd_sync_p = True;
else if (!strcmp (argv[i], "-kbd-async") ||
!strcmp (argv[i], "-keyboard-async"))
kbd_sync_p = False;
else if (!strcmp (argv[i], "-mouse-sync") ||
!strcmp (argv[i], "-pointer-sync"))
mouse_sync_p = True;
else if (!strcmp (argv[i], "-mouse-async") ||
!strcmp (argv[i], "-pointer-async"))
mouse_sync_p = False;
else
{
fprintf (stderr, "%s: unknown option: %s\n", blurb(), oa);
exit (1);
}
}
toplevel_shell = XtAppInitialize (&app, progclass, 0, 0,
&argc, argv, 0, 0, 0);
dpy = XtDisplay (toplevel_shell);
if (!dpy) exit(1);
if (! init_xinput (dpy, &xi_opcode))
exit (1);
if (grab_kbd_p || grab_mouse_p)
{
int timeout = 15;
Window w = RootWindow (dpy, 0);
int status;
XColor black = { 0, };
Pixmap bit = XCreateBitmapFromData (dpy, w, "\000", 1, 1);
Cursor cursor = XCreatePixmapCursor (dpy, bit, bit, &black, &black, 0, 0);
if (grab_kbd_p)
{
status = XGrabKeyboard (dpy, w, True,
(mouse_sync_p ? GrabModeSync : GrabModeAsync),
(kbd_sync_p ? GrabModeSync : GrabModeAsync),
CurrentTime);
if (status == GrabSuccess)
fprintf (stderr, "%s: grabbed keyboard (%s, %s)\n", blurb(),
(mouse_sync_p ? "sync" : "async"),
(kbd_sync_p ? "sync" : "async"));
else
{
fprintf (stderr, "%s: failed to grab keyboard (%s, %s): %s\n",
blurb(),
(mouse_sync_p ? "sync" : "async"),
(kbd_sync_p ? "sync" : "async"),
grab_string (status));
exit(1);
}
}
if (grab_mouse_p)
{
status = XGrabPointer (dpy, w, True,
(ButtonPressMask | ButtonReleaseMask |
EnterWindowMask | LeaveWindowMask |
PointerMotionMask | PointerMotionHintMask |
Button1MotionMask | Button2MotionMask |
Button3MotionMask | Button4MotionMask |
Button5MotionMask | ButtonMotionMask),
(mouse_sync_p ? GrabModeSync : GrabModeAsync),
(kbd_sync_p ? GrabModeSync : GrabModeAsync),
w, cursor, CurrentTime);
if (status == GrabSuccess)
fprintf (stderr, "%s: grabbed mouse (%s, %s)\n", blurb(),
(mouse_sync_p ? "sync" : "async"),
(kbd_sync_p ? "sync" : "async"));
else
{
fprintf (stderr, "%s: failed to grab mouse (%s, %s): %s\n",
blurb(),
(mouse_sync_p ? "sync" : "async"),
(kbd_sync_p ? "sync" : "async"),
grab_string (status));
exit(1);
}
}
fprintf (stderr, "%s: ungrabbing in %d seconds\n", blurb(), timeout);
XtAppAddTimeOut (app, 1000 * timeout, ungrab_timer, (XtPointer) dpy);
}
while (1)
{
XEvent xev;
XIRawEvent *re;
XtAppNextEvent (app, &xev);
XtDispatchEvent (&xev);
switch (xev.xany.type) {
case KeyPress:
case KeyRelease:
{
static XComposeStatus compose = { 0, };
KeySym keysym = 0;
char c[100];
int n;
n = XLookupString (&xev.xkey, c, sizeof(c)-1, &keysym, &compose);
c[n] = 0;
fprintf (stderr, "%s: X11 Key%s %02x %02x %s \"%s\"\n", blurb(),
(xev.xkey.type == KeyPress ? "Press " : "Release"),
xev.xkey.keycode, xev.xkey.state,
XKeysymToString (keysym), c);
}
break;
case ButtonPress:
case ButtonRelease:
fprintf (stderr, "%s: X11 Button%s %d %d\n", blurb(),
(xev.xany.type == ButtonPress ? "Press " : "Release"),
xev.xbutton.button, xev.xbutton.state);
break;
case MotionNotify:
fprintf (stderr, "%s: X11 MotionNotify %4d, %-4d\n",
blurb(), xev.xmotion.x_root, xev.xmotion.y_root);
break;
case GenericEvent:
break;
case EnterNotify:
case LeaveNotify:
break;
default:
fprintf (stderr, "%s: X11 event %d on 0x%lx\n",
blurb(), xev.xany.type, xev.xany.window);
break;
}
if (xev.xcookie.type != GenericEvent ||
xev.xcookie.extension != xi_opcode)
continue; /* not an XInput event */
if (!xev.xcookie.data)
XGetEventData (dpy, &xev.xcookie);
if (!xev.xcookie.data)
continue; /* Bogus XInput event */
re = xev.xcookie.data;
switch (xev.xcookie.evtype) {
case XI_RawKeyPress:
case XI_RawKeyRelease:
{
/* Fake up an XKeyEvent in order to call XKeysymToString(). */
XEvent ev2;
Bool ok = xinput_event_to_xlib (xev.xcookie.evtype,
(XIDeviceEvent *) re,
&ev2);
if (!ok)
fprintf (stderr, "%s: unable to translate XInput2 event\n",
blurb());
else
{
static XComposeStatus compose = { 0, };
KeySym keysym = 0;
char c[100];
int n;
n = XLookupString (&ev2.xkey, c, sizeof(c)-1, &keysym, &compose);
c[n] = 0;
fprintf (stderr, "%s: XI_RawKey%s %02x %02x %s \"%s\"\n",
blurb(),
(ev2.xkey.type == KeyPress ? "Press " : "Release"),
ev2.xkey.keycode, ev2.xkey.state,
XKeysymToString (keysym), c);
}
}
break;
case XI_RawButtonPress:
case XI_RawButtonRelease:
fprintf (stderr, "%s: XI_RawButton%s %d\n", blurb(),
(re->evtype == XI_RawButtonPress ? "Press " : "Release"),
re->detail);
break;
case XI_RawMotion:
{
Window root_ret, child_ret;
int root_x, root_y;
int win_x, win_y;
unsigned int mask;
XQueryPointer (dpy, DefaultRootWindow (dpy),
&root_ret, &child_ret, &root_x, &root_y,
&win_x, &win_y, &mask);
fprintf (stderr, "%s: XI_RawMotion %4d, %-4d %7.02f, %-7.02f\n",
blurb(),
root_x, root_y,
re->raw_values[0], re->raw_values[1]);
}
break;
case XI_RawTouchBegin:
fprintf (stderr, "%s: XI_RawTouchBegin\n", blurb());
break;
case XI_RawTouchEnd:
fprintf (stderr, "%s: XI_RawTouchEnd", blurb());
break;
case XI_RawTouchUpdate:
fprintf (stderr, "%s: XI_RawTouchUpdate", blurb());
break;
default:
fprintf (stderr, "%s: XInput unknown event %d\n", blurb(),
xev.xcookie.evtype);
break;
}
XFreeEventData (dpy, &xev.xcookie);
}
exit (0);
}