The overall goal is like Windows' Alt-Tab, so I will use these keys for explanation. I want:
Press Alt -> press Tab -> [ popup appears ] -> press Tab any time holding Alt -> release Alt -> [ popup disappears ].
I can't detect final Alt release.
Trivial approach: grabbing Alt-Tab:
XGrabKey (dpy,
XKeysymToKeycode(dpy,XK_Tab), Mod1Mask,
root, True, GrabModeAsync, GrabModeAsync);
(full code: http://pastebin.com/K2P65KJn)
Result:
[ Alt pressed ]
[ Tab pressed ]
Pressing Alt-Tab reported
[ Tab released ]
Releasing Alt-Tab reported
[ Alt released ] -> nothing reported
Grabbing both Alt-Tab and Any-Alt:
XGrabKey (dpy,
XKeysymToKeycode(dpy,XK_Tab), Mod1Mask,
root, True, GrabModeAsync, GrabModeAsync);
XGrabKey (dpy,
XKeysymToKeycode(dpy,XK_Alt_L), AnyModifier,
root, True, GrabModeAsync, GrabModeAsync);
(full code: http://pastebin.com/75mD1tjA)
It works!
[ Alt pressed ]
Pressing Alt reported
[ Tab pressed ]
Pressing Alt-Tab reported
[ Tab released ]
Releasing Alt-Tab reported
[ Alt released ]
Releasing Alt-Alt reported
But this hides any Alt combination from any running program.
I can't find the way to push back events which don't belong to us (tried XSendEvent), and overally grabbing Alt from the start looks too invasive.
Grabbing Alt after first Alt-Tab press, then ungrab Alt after its release.
Unfortunately, first Alt release is still not reported:
[ Alt pressed ]
[ Tab pressed ]
Pressing Alt-Tab reported, Alt grabbed here
[ Tab released ]
Releasing Alt-Tab reported
[ Alt released ] -> nothing reported! subsequent Alt press/release are reported, though not useful:
[ Alt pressed ]
Pressing Alt reported
...
Do I need to mess with low-level xinput or there is another way to achieve the goal?
It looks like you won't get a KeyRelease event if you have registered interest in it after the key was pressed.
I can think of two different ways around this.
Select KeyReleaseMask for all windows (and keep track of appearing and disappearing windows); or
Once you know Alt is pressed, poll the keyboard state with XQueryKeyboard every 0.1 second or so until it's released.
I have tested the first method and it seems to be working:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdbool.h>
#include <stdio.h>
void dowin (Display* dpy, Window win, int reg)
{
Window root, parent;
Window* children;
int nchildren, i;
XSelectInput (dpy, win, reg ? KeyReleaseMask|SubstructureNotifyMask : 0);
XQueryTree (dpy, win, &root, &parent, &children, &nchildren);
for (i = 0; i < nchildren; ++i)
{
dowin (dpy, children[i], reg);
}
XFree(children);
}
int main()
{
Display* dpy = XOpenDisplay(0);
Window win = DefaultRootWindow(dpy);
XEvent ev;
unsigned int alt_modmask = Mod1Mask;
unsigned int ignored_modmask = 0; // stub
KeyCode tab_keycode = XKeysymToKeycode(dpy,XK_Tab);
KeyCode alt_keycode = XKeysymToKeycode(dpy,XK_Alt_L);
dowin (dpy, win, True);
XGrabKey (dpy,
tab_keycode,
alt_modmask | ignored_modmask,
win,
True,
GrabModeAsync, GrabModeAsync);
while(true)
{
ev.xkey.keycode = 0;
ev.xkey.state = 0;
ev.xkey.type = 0;
XNextEvent(dpy, &ev);
switch(ev.type)
{
case KeyPress:
printf ("Press %x: d-%d\n", ev.xkey.window, ev.xkey.state, ev.xkey.keycode);
break;
case KeyRelease:
printf ("Release %x: %d-%d\n", ev.xkey.window, ev.xkey.state, ev.xkey.keycode);
break;
case MapNotify:
printf ("Mapped %x\n", ev.xmap.window);
dowin (dpy, ev.xmap.window, True);
break;
case UnmapNotify:
printf ("Unmapped %x\n", ev.xunmap.window);
dowin (dpy, ev.xunmap.window, False);
break;
default:
printf ("Event type %d\n", ev.type);
break;
}
}
XCloseDisplay(dpy);
return 0;
}
Related
I am using "XGrabPointer" to get the mouse click events when ever they occured in the active window.But my requirement is to detect the clicks globally i.e in any application on the X11 desktop.
XGrabPointer blocks the active window so i can not move to other applications and detect the mouse click events.
Here are the codes:
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
int main(int argc, char **argv)
{
Display *display;
XEvent xevent;
Window window;
int grb;
int scr;
if( (display = XOpenDisplay(NULL)) == NULL )
return -1;
unsigned int t_new=0,t_prev=0,t_diff=0;
scr = DefaultScreen(display);
window = RootWindow(display, scr);
while(1) {
XGrabPointer(display,
window,
True,
PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
GrabModeAsync,
GrabModeAsync,
None,
None,
CurrentTime);
XAllowEvents(display,AsyncPointer, CurrentTime);
XNextEvent(display, &xevent);
switch (xevent.type) {
case MotionNotify:{
printf("motion event\n");
break;
}
case ButtonPress:{
switch (xevent.xbutton.button) {
case 1:
printf("Left Click\n");
t_prev=t_new;
printf("Click Occured : [%d, %d]\n",
xevent.xbutton.x_root,
xevent.xbutton.y_root);
break;
case 2:
printf("Grabed\n");
printf("Middle Click\n");
break;
case 3:
printf("Right Click\n");
break;
case 4:
printf("Grabed\n");
printf("Scroll UP\n");
break;
case 5:
printf("Scroll Down\n");
break;
}
break;
}
}
}
XUngrabPointer(display,CurrentTime);
return 0;
}
Couldn't find an answer on how to listen to mouse events in the background as well. It's impossible to do it with mouse grabbing and you won't be able to click anywhere outside of your program.
So the solution is to read linux's /dev/input/mice device for the raw mouse input (we want button clicks) and when a low-level event occur we query X server for mouse position (can't query mouse key presses from X this way).
Display *display;
Window root_window;
XEvent event;
display = XOpenDisplay(0);
root_window = DefaultRootWindow(display);
int fd, bytes;
unsigned char data[3];
const char *pDevice = "/dev/input/mice";
// Open Mouse
fd = open(pDevice, O_RDWR);
if (fd == -1) {
printf("ERROR Opening %s\n", pDevice);
return -1;
}
int left, middle, right;
while (1) {
// Read Mouse
bytes = read(fd, data, sizeof(data));
if (bytes > 0) {
left = data[0] & 0x1;
right = data[0] & 0x2;
middle = data[0] & 0x4;
XQueryPointer(
display,
root_window,
&event.xbutton.root,
&event.xbutton.subwindow,
&event.xbutton.x_root,
&event.xbutton.y_root,
&event.xbutton.x,
&event.xbutton.y,
&event.xbutton.state
);
printf("x=%d, y=%d, left=%d, middle=%d, right=%d\n", event.xmotion.x, event.xmotion.y, left, middle, right);
}
}
Sample output
x=470, y=969, left=1, middle=0, right=0
x=470, y=969, left=0, middle=0, right=0
x=467, y=969, left=0, middle=4, right=0
x=463, y=969, left=0, middle=0, right=0
x=444, y=971, left=0, middle=0, right=2
x=441, y=971, left=0, middle=0, right=0
I would like to make a key press translator that would "convert" one key press to another, eg. Ctrl+T would be translated to Ctrl+X. I would like it to make "gobal"; to make it work in any application.
As a proof of concept I'm experimenting with the code below but it doesn't work. The problem is that when I capture a key press I send out another key press that is again captured by my code... I'm getting an infinite loop (the counter is there only to break out of the infinite loop).
How should I send out key press event from a key press event handler?
#include <stdio.h>
#include <X11/Xlib.h>
#include <xdo.h>
#include <X11/extensions/XTest.h>
int main(void)
{
Display *dpy = XOpenDisplay(0x0);
XEvent ev;
int counter;
xdo_t *xdo = xdo_new(NULL);
XGrabKeyboard(dpy, DefaultRootWindow(dpy), False,
GrabModeAsync, GrabModeAsync,CurrentTime);
for(counter = 0; counter < 10; counter++)
{
XNextEvent(dpy, &ev);
if(ev.type == KeyPress) {
XUngrabKeyboard(dpy, CurrentTime);
printf("%d %d\n", ev.xkey.keycode, ev.xany.send_event);
xdo_keysequence(xdo, CURRENTWINDOW, "A", 0);
}
}
return 0;
}
I have an idea for a project which needs to be run on a touch screen device. The idea is to have a button on screen which when pressed switches between open projects. So exactly how the ALT + TAB keyboard shortcut works.
I know the SendKeys::Send() event in C++ can simulate key presses but it doesn't seem to work for me when I try sending ALT + TAB. So is there a way that I can have the window displaying all open programs (just like when ALT TAB is pressed) through C++ ?
PS The project is a Windows application! Windows 7 to begin but hopefully it can be compatible with more Windows systems later.
Assuming C++/CLI since you mentioned SendKeys. SendKeys cannot work reliably because it releases the keys, making the Alt-Tab window disappear. You want to use SendInput() instead and send a keydown for the Alt key and a keydown + up for the Tab key. This code worked well:
#include <windows.h>
#pragma comment(lib, "user32.lib")
...
System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
INPUT input = {INPUT_KEYBOARD};
input.ki.wVk = (WORD)Keys::Menu;
UINT cnt = SendInput(1, &input, sizeof(input));
input.ki.wVk = (WORD)Keys::Tab;
if (cnt == 1) cnt = SendInput(1, &input, sizeof(input));
input.ki.dwFlags = KEYEVENTF_KEYUP;
if (cnt == 1) cnt = SendInput(1, &input, sizeof(input));
if (cnt != 1) throw gcnew System::ComponentModel::Win32Exception;
}
My goal is to have a program that sleeps in the background but can be activated by the user via some "hotkey". From digging around the Xlib manual and the Xlib O'reilly manual, I gather that the correct way to to this is with XGrabKey. However my understanding of the process is incorrect as a simple proof of concept does not work.
My understanding is that if I call XGrabKey with the root window as the grab_window, and owner_events false, then whenever my hotkey is pressed the event will be sent only to the root window. If I then select KeyPress events from the root window, and then listen for X events, I should get a key press event when the hotkey is pressed. I've pasted a minimal example below.
What I expect is that when the program is run, regardless of what window has focus, if Ctrl+Shift+K is pressed, my program should output "Hot key pressed!" in the console, and then terminate.
Furthermore, it is my understanding that if the XGrabKey fails, the default error handler will display a message, and since it does not I am assuming that the call succeeds.
Obviously, my understanding is flawed somehow. Can anyone point me in the right direction?
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;
int main()
{
Display* dpy = XOpenDisplay(0);
Window root = DefaultRootWindow(dpy);
XEvent ev;
unsigned int modifiers = ControlMask | ShiftMask;
int keycode = XKeysymToKeycode(dpy,XK_Y);
Window grab_window = root;
Bool owner_events = False;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode,
keyboard_mode);
XSelectInput(dpy, root, KeyPressMask );
while(true)
{
bool shouldQuit = false;
XNextEvent(dpy, &ev);
switch(ev.type)
{
case KeyPress:
cout << "Hot key pressed!" << endl;
XUngrabKey(dpy,keycode,modifiers,grab_window);
shouldQuit = true;
default:
break;
}
if(shouldQuit)
break;
}
XCloseDisplay(dpy);
return 0;
}
Your program works here. My guess is you have another modifier active, such as NumLock. GrabKey only works on the exact modifier mask.
For example here is some (GPL) code from metacity window manager
/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */
static void
meta_change_keygrab (MetaDisplay *display,
Window xwindow,
gboolean grab,
int keysym,
unsigned int keycode,
int modmask)
{
unsigned int ignored_mask;
/* Grab keycode/modmask, together with
* all combinations of ignored modifiers.
* X provides no better way to do this.
*/
meta_topic (META_DEBUG_KEYBINDINGS,
"%s keybinding %s keycode %d mask 0x%x on 0x%lx\n",
grab ? "Grabbing" : "Ungrabbing",
keysym_name (keysym), keycode,
modmask, xwindow);
/* efficiency, avoid so many XSync() */
meta_error_trap_push (display);
ignored_mask = 0;
while (ignored_mask <= display->ignored_modifier_mask)
{
if (ignored_mask & ~(display->ignored_modifier_mask))
{
/* Not a combination of ignored modifiers
* (it contains some non-ignored modifiers)
*/
++ignored_mask;
continue;
}
if (meta_is_debugging ())
meta_error_trap_push_with_return (display);
if (grab)
XGrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow,
True,
GrabModeAsync, GrabModeSync);
else
XUngrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow);
if (meta_is_debugging ())
{
int result;
result = meta_error_trap_pop_with_return (display, FALSE);
if (grab && result != Success)
{
if (result == BadAccess)
meta_warning (_("Some other program is already using the key %s with modifiers %x as a binding\n"), keysym_name (keysym), modmask | ignored_mask);
else
meta_topic (META_DEBUG_KEYBINDINGS,
"Failed to grab key %s with modifiers %x\n",
keysym_name (keysym), modmask | ignored_mask);
}
}
++ignored_mask;
}
meta_error_trap_pop (display, FALSE);
}
With your mask ControlMask | ShiftMask you will not get the key if another modifier key is held. This sounds okay in the first place, but there's a pitfall: NumLock, CapsLock and alike all are treated as modifiers, too.
You have two options:
You call XGrabKey() multiple times, once for each explicit combination that you're interested in.
You call XGrabKey() with AnyModifier and use event.xkey.state to check whether the modifiers are as you expected.
The header file <X.h> defines ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask and AnyModifier.
The keys are:
Mask | Value | Key
------------+-------+------------
ShiftMask | 1 | Shift
LockMask | 2 | Caps Lock
ControlMask | 4 | Ctrl
Mod1Mask | 8 | Alt
Mod2Mask | 16 | Num Lock
Mod3Mask | 32 | Scroll Lock
Mod4Mask | 64 | Windows
Mod5Mask | 128 | ???
Warning I found out about the ModNMask keys by trying and I do not know if this is valid on all machines / configurations / versions / operating systems.
In your case, you probably want to make sure that ShiftMask | CtrlMask is set, Mod1Mask | Mod4Mask are clear, and the others to be ignored.
I'd do this to setup the key grab:
XGrabKey(dpy, keycode, AnyModifier, grab_window, owner_events, pointer_mode, keyboard_mode);
And this to check whether the right modifiers are set:
switch (ev.type) {
case KeyPress:
if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask))
// ...
}
If you're using/targeting gtk on X11, there's a C library with a much simpler interface:
https://github.com/engla/keybinder
Includes Python, Lua and Vala bindings. (Also mentioned here.)
I have written a simple program which will report key press and release events for a particular window. In my case, it is mostly the terminal since I invoke the program from the terminal. I am able to get the key press and release events taking place in the terminal window (I have used XSelectInput() with KeyPressMask and KeyReleaseMask on the terminal) but the same is not working with ButtonPress and ButtonRelease. Not just these, but any events related to the mouse are not being reported. Any idea why this is happening?
#include
#include
#include
#include
#include
#include
int main() {
Display *display = XOpenDisplay(NULL);
KeySym k;
int revert_to;
Window window;
XEvent event;
XGetInputFocus(display, &window, &revert_to);
XSelectInput(display, window, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
while(1)
{
XNextEvent(display,&event);
switch (event.type) {
case KeyPress : printf("Key Pressed\n"); break;
case KeyRelease : printf("Key Released\n"); break;
case ButtonPress : printf("Button Pressed\n"); break;
case ButtonRelease : printf("Button Released\n"); break;
case EnterNotify : printf("Enter\n"); break;
}
}
XCloseDisplay(display);
return 0;
}
The problem you encounter is that Xlib sends ButtonPress/Release events to only one client. I think that the window you're working with already has a client which is listening to its mouse events. Therefore your SelectInput call did not actually set ButtonPress/Release masks and generated an error which you didn't check for.