How to detect Shift + Tab in Xlib? I'm able to match KeySym with XK_Tab, but it's not matching while holding shift to then check for ev->state & ShiftMask, so I'm kind of lost.
SOLUTION:
Thanks to Erdal Küçük's answer I was able to come up with the following function that detects Shift + Tab:
int detectShiftTab(XKeyEvent *xkey) {
return XLookupKeysym(xkey, 0) == XK_Tab && xkey->state & ShiftMask;
}
With a simple test, i realized that the ShiftMask is not reported on a KeyPress of the Shift key. Other than that, the ShiftMask is always set.
int main()
{
Display *dpy = XOpenDisplay(NULL);
Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 640, 480, 0, 0, 0);
XSetWindowAttributes attr = { .event_mask = (KeyPressMask | KeyReleaseMask) };
XChangeWindowAttributes(dpy, win, CWEventMask, &attr);
XMapWindow(dpy, win);
while (1) {
XEvent evt;
XNextEvent(dpy, &evt);
switch (evt.type) {
case KeyPress:
printf(
"key press: %s shiftmask: %d\n",
XKeysymToString(XLookupKeysym(&evt.xkey, 0)),
(evt.xkey.state & ShiftMask) == ShiftMask
);
break;
case KeyRelease:
printf(
"key release: %s shiftmask: %d\n",
XKeysymToString(XLookupKeysym(&evt.xkey, 0)),
(evt.xkey.state & ShiftMask) == ShiftMask
);
break;
}
}
return 0;
}
I get the following output (typing Shift + Tab):
key press: Shift_L shiftmask: 0
key press: Tab shiftmask: 1
key release: Tab shiftmask: 1
key release: Shift_L shiftmask: 1
Related
I am making a simple xlib wrapper for recreation, but I am having a problem with X11 since I activated WSL: At the start of some programs, it starts sending a specific key press event and when I press something it stops.
I tried looking at some documentation and even debug input files on /dev/, but seems that WSL don't have them, at least I didn't found.
My window_init():
Display* display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask | KeyReleaseMask;
Window window = XCreateWindow(display, root, 0, 0, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, CWEv>
XStoreName(display, window, name);
XMapWindow(display, window);
XFlush(display);
My window_update():
XEvent event;
while (XPending(display->display)) {
char k;
XNextEvent(display->display, &event);
switch (event.type) {
case KeyPress:
k = XLookupKeysym(&event.xkey, 0)-XK_a;
if(k >= 0 && k<= 26){
keys_down[k] = 1;
pressed[k] = 1;
}
break;
}
}
I still didn't handle the release events property, but at the start of the execution the program dispatches lots of keypresses events.
Here is a fully verified working example of the same:
// SPDX-License-Identifier: CC0-1.0
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
int main(void)
{
Display *display;
Window root, window;
XSetWindowAttributes attrs;
XEvent event;
Atom close_window;
display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "No display.\n");
return EXIT_FAILURE;
}
root = DefaultRootWindow(display);
attrs.event_mask = ExposureMask | PointerMotionMask | KeyPressMask | KeyReleaseMask;
window = XCreateWindow(display, root,
100, 100, 320, 240,
0, CopyFromParent, InputOutput, CopyFromParent,
CWEventMask, &attrs);
XStoreName(display, window, "Example Program");
close_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &close_window, 1);
XMapWindow(display, window);
XFlush(display);
while (1) {
XNextEvent(display, &event);
if (event.type == ClientMessage && (Atom)(event.xclient.data.l[0]) == close_window)
break;
switch (event.type) {
case MotionNotify:
// It's just mouse motion events, so we won't mention those.
break;
case KeyPress:
printf("KeyPress event: state = %u (", event.xkey.state);
if (event.xkey.state & ShiftMask) printf(" Shift");
if (event.xkey.state & LockMask) printf(" CapsLock");
if (event.xkey.state & ControlMask) printf(" Control");
if (event.xkey.state & Mod1Mask) printf(" Mod1");
if (event.xkey.state & Mod2Mask) printf(" Mod2");
if (event.xkey.state & Mod3Mask) printf(" Mod3");
if (event.xkey.state & Mod4Mask) printf(" Mod4");
if (event.xkey.state & Mod5Mask) printf(" Mod5");
printf(" ), keycode = %u\n", event.xkey.keycode);
break;
case KeyRelease:
printf("KeyRelease event: state = %u (", event.xkey.state);
if (event.xkey.state & ShiftMask) printf(" Shift");
if (event.xkey.state & LockMask) printf(" CapsLock");
if (event.xkey.state & ControlMask) printf(" Control");
if (event.xkey.state & Mod1Mask) printf(" Mod1");
if (event.xkey.state & Mod2Mask) printf(" Mod2");
if (event.xkey.state & Mod3Mask) printf(" Mod3");
if (event.xkey.state & Mod4Mask) printf(" Mod4");
if (event.xkey.state & Mod5Mask) printf(" Mod5");
printf(" ), keycode = %u\n", event.xkey.keycode);
break;
case Expose:
printf("Expose event: x=%d, y=%d, width=%d, height=%d, count=%d\n", event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height, event.xexpose.count);
break;
default:
printf("Event type %d\n", event.type);
}
fflush(stdout);
}
XCloseDisplay(display);
return EXIT_SUCCESS;
}
When compiling, link against the X11 library.
Since Expose is not implemented, it is normal and expected for the contents of the window to be garbage (solid color, copy of existing window data, or anything else; it varies depending on X server and window manager).
If you run the program from the command line in Linux, and you keep Enter pressed, the first KeyPress event you get is the first autorepeat event. You do sometimes get the KeyRelease event corresponding to the Enter key being released after the window has been mapped, but before any autorepeat events. The first described event is usually the Expose event,
Expose event: x=0, y=0, width=320, height=240, count=0
occasionally followed by the KeyRelease event corresponding to the Enter key,
KeyRelease event: state = 8192 ( ), keycode = 36
but one should ignore any KeyRelease events anyway that do not correspond to previous KeyPress event, so that shouldn't be an issue. (Shift is Shift, Lock is CapsLock (when on/enabled), Control is Ctrl, Mod1 is Alt, Mod5 AltGr, Mod4 ⊞ (Windows key).)
The close_window atom is used to handle the window close button. When the button is pressed, the window manager will send a ClientMessage event containing the same atom.
If you get any KeyPress events corresponding to keypresses that occurred prior to the start of the program, it is a bug in WSL that you need to report to Microsoft. (Only KeyRelease events corresponding to keys that are released after the program started, and autorepeat KeyPress events corresponding to keys that are being pressed down when the program starts and afterwards, are expected to occur.)
(Personally, instead of WSL, I would recommend running a real Linux installation in a virtual machine in Windows, unless you explicitly intend your programs to be run under WSL instead of actual Linux installations. There just seem to be too many bugs in WSL, in my opinion; plus one can snapshot and start and stop the virtual machine as needed. Others disagree, of course.)
I have a program that draws on a x11 window and I'd like to quit it as soon as I press any key.
With the code I have, I cannot stop the program with a keypress until the for loop has finished drawing on the window (moving a red oval from left to right). I'd like to quit the for loop as soon as a key is pressed regardless of the state of the drawing (finished or not) even if the for loop hasn't finished executing its part.
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <unistd.h>
Display *dis;
Window win;
unsigned long GetColor(char *color_name)
{
Colormap cmap;
XColor near_color, true_color;
cmap = DefaultColormap(dis, 0);
XAllocNamedColor(dis, cmap, color_name, &near_color, &true_color);
return(near_color.pixel);
}
int main(int argc, char **argv)
{
if ((dis = XOpenDisplay(NULL)) == NULL) { printf("Error XOpenDisplay\n"); return 1; }
win = XCreateSimpleWindow(dis, RootWindow(dis, 0), 1, 1, 256, 256, 0, BlackPixel(dis, 0), BlackPixel(dis, 0));
XSelectInput(dis, win, ExposureMask | KeyPressMask | KeyReleaseMask);
XMapWindow(dis, win);
XFlush(dis);
XEvent ev;
do {
XNextEvent(dis, &ev);
} while (ev.type != Expose);
GC gc;
gc = XCreateGC(dis, DefaultRootWindow(dis), 0, 0);
XSetFunction(dis, gc, GXxor);
while (!(XCheckWindowEvent(dis, win, KeyPressMask, &ev) || XCheckTypedWindowEvent(dis, win, ClientMessage, &ev))) {
XNextEvent(dis, &ev);
// quit program as soon as a key is pressed
if (ev.type == KeyPress && XEventsQueued(dis, QueuedAfterReading)) {
goto finish;
}
// I'd like to quit from this for loop as soon as a key is pressed
for (int t = 0; t < 150; t++) {
// quit program as soon as a key is pressed
if (ev.type == KeyPress && XEventsQueued(dis, QueuedAfterReading)) {
goto finish;
} else {
// draw a red oval and move it from left to right
XSetForeground(dis, gc, BlackPixel(dis, 0) ^ GetColor("red"));
XFillArc(dis, win, gc, t * 5 + 10, t * 3 + 40, 80, 40, 0, 360 * 64);
XSetForeground(dis, gc, BlackPixel(dis, 0) ^ GetColor("red"));
usleep(20000);
XFillArc(dis, win, gc, t * 5 + 10, t * 3 + 40, 80, 40, 0, 360 * 64);
//XSync(dis, True);
}
}
}
finish:
XFreeGC(dis, gc);
XDestroyWindow(dis, win);
XCloseDisplay(dis);
return(0);
}
I execute it with:
gcc draw.c -lX11 && ./a.out
The issue you are facing has to do with usleep.
Basically, usleep will suspend the thread that the program runs on until the set interval will finish. You can interrupt it by sending a signal (e.g. SIGKILL or SIGQUIT) to it, but this is something that can be done only outside of the program, not from within.
You may want to see this page to understand how usleep works.
How does one create a native X11 window that works in EGL? Going through the eglIntro there is little documentation on the matter. Alternatively, is there a way to create native windows through EGL itself? There is an EGLNativeWindowType that I assume can be used instead of whatever X11's native window types are.
No, EGL itself does not provide an Xlib wrapper. You have to create the window by yourself.
Here is a minimal sample to get you started. It refers to GLES2, but it should work with GLES1 also.
First, you declare the Xlib objects(display and window).
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <GLES2/gl2.h>
#include <EGL/egl.h>
// Native handles for window and display
Window win;
Display* xdisplay;
// EGL-related objects
EGLDisplay egl_display;
EGLConfig egl_conf;
EGLContext egl_context;
EGLSurface egl_surface;
int init_egl()
{
EGLint attr[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
// EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, /* If one needs GLES2 */
EGL_NONE
};
EGLint num_config;
EGLint major, minor;
EGLint ctxattr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
egl_display = eglGetDisplay( (EGLNativeDisplayType) xdisplay );
if ( egl_display == EGL_NO_DISPLAY ) {
printf("Error getting EGL display\n");
return 0;
}
if ( !eglInitialize( egl_display, &major, &minor ) ) {
printf("Error initializing EGL\n");
return 0;
}
printf("EGL major: %d, minor %d\n", major, minor);
/* create EGL rendering context */
if ( !eglChooseConfig( shell->egl_display, attr, &shell->egl_conf, 1, &num_config ) ) {
printf("Failed to choose config (eglError: %x)\n", eglGetError());
return 0;
}
if ( num_config != 1 ) {
return 0;
}
egl_surface = eglCreateWindowSurface ( egl_display, egl_conf, win, NULL );
if (egl_surface == EGL_NO_SURFACE ) {
printf("CreateWindowSurface, EGL eglError: %d\n", eglGetError() );
return 0;
}
egl_context = eglCreateContext ( egl_display, egl_conf, EGL_NO_CONTEXT, ctxattr );
if ( egl_context == EGL_NO_CONTEXT ) {
printf("CreateContext, EGL eglError: %d\n", eglGetError() );
return 0;
}
return 1;
}
From the main function you will call the X event handler procedure. I have left commented printf calls to show how the event values are named, so there's no need to lookup the documentation. If the concept of "Event loop" is not clear, then I recommend reading about general UI event processing.
void process_xevent(XEvent xev) {
// XNextEvent( xdisplay, &xev );
switch (xev.type)
{
case MotionNotify:
// printf("motion: %d %d\n", xev.xbutton.x, xev.xbutton.y);
break;
case KeyRelease:
// printf("rel (%d)\n", XLookupKeysym (&xev.xkey, 0));
break;
case KeyPress:
// printf("keypress (%d)\n", XLookupKeysym (&xev.xkey, 0));
break;
case ButtonPress:
// printf("BPress: state = %d, button = %d, x = %d, y = %d\n", xev.xbutton.state, xev.xbutton.button, xev.xbutton.x, xev.xbutton.y);
// printf("Type=%d\n", (int)xev.xbutton.type);
break;
case ButtonRelease:
// printf("BRelease: state = %d, button = %d, x = %d, y = %d\n", xev.xbutton.state, xev.xbutton.button, xev.xbutton.x, xev.xbutton.y);
// printf("Type=%d\n", (int)xev.xbutton.type);
break;
}
}
Finally, in the main() routine you create and open display/Xwindow.
int main()
{
int egl_error;
Window root;
XSetWindowAttributes swa;
/* open standard display (primary screen) */
xdisplay = XOpenDisplay ( NULL );
if ( xdisplay == NULL ) {
printf("Error opening X display\n");
return 0;
}
The window is created and shown once you have the Display opened.
Finally, in the main() routine you create and open display/Xwindow.
// get the root window (usually the whole screen)
root = DefaultRootWindow( shell->xdisplay );
// list all events this window accepts
swa.event_mask =
StructureNotifyMask |
ExposureMask |
PointerMotionMask |
KeyPressMask |
KeyReleaseMask |
ButtonPressMask |
ButtonReleaseMask;
// Xlib's window creation
win = XCreateWindow (
xdisplay, root, 0, 0, 640, 480, 0,
CopyFromParent, InputOutput, CopyFromParent, CWEventMask,
&swa );
XMapWindow ( xdisplay , win ); // make window visible
XStoreName ( xdisplay , win , "EGL" );
When you have the window, intialise the EGL.
egl_error = init_egl();
if (!egl_error) {
return 1;
}
Once you have the EGL & Xlib objects, you can start the event processing loop.
while (1) {
int keycode;
XEvent xev;
if ( XPending ( xdisplay ) )
if (XCheckWindowEvent(shell->xdisplay, shell->win, global_event_mask, &xev))
process_xevent(shell, xev);
/* if (should_exit) { break; } // set some global flag if you want to exit */
eglMakeCurrent( egl_display, egl_surface, egl_surface, egl_context );
/* Call OpenGL as you see fit */
/* get rendered buffer to the screen */
eglSwapBuffers ( egl_display, egl_surface );
}
// deinitialize
}
This should get you started. The code is extracted from a larger project, so there might have be typos introduced while removing irrelevant things.
To conclude the answer and to be more precise, here EGLNativeWindowType is specialized to Window from X11/Xlib.h header and EGLNativeDisplayType is Display*.
An easier way might be to use libxcb, but I do not have any tested sample code. The GLFW library can be a useful source of OS-dependant OpenGL context creation routines.
I would like to open a "dock type" window without its title bar with x11 in C and I want to capture a key press (Ctrl-q) to close it. The actual code does not capture the key presses to the active window as they are all sent and printed in the terminal. Almost all the Ctrl key presses are somehow not detected. The only key presses that actually work on the opened window are Ctrl-c. I am aware of the _MOTIF_WM_HINTS property but it has the disadvantage to display a window icon in the panel to show a new opened window.
How can I have a "dock type" window while not losing the ability to detect key presses to the active window?
Here is the code basically copied from two other posts here and here
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
int main(int argc, char **argv)
{
Display *dpy = XOpenDisplay(0);
int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, blackColor, blackColor);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
Atom window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
long value = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(dpy, w, window_type, 4, 32,
PropModeReplace,
(unsigned char *)&value, 1);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
if(e.xkey.state == ControlMask && key == XK_q) {
printf("CTRL-Q\n");
break;
}
}
}
}
if (dpy && gc) XFreeGC(dpy, gc);
if (dpy && w) XDestroyWindow(dpy, w);
if (dpy) XCloseDisplay(dpy);
return 0;
}
Compiled with:
gcc main.c -L/usr/X11R6/lib -lX11 -lm && ./a.out
I've also looked at the _NET_WM_STRUT_PARTIAL property but I haven't been succesful at implementing it.
This is a working example of X11 code that handles Ctrl-q event to quit application:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
void exitOnCondition(char cond, const char *msg, int exitCode, Display *dpy, Window *w, GC *gc) {
if(cond) {
printf("%s\n", msg);
if(dpy && gc) XFreeGC(dpy, *gc);
if(dpy && w) XDestroyWindow(dpy, *w);
if(dpy) XCloseDisplay(dpy);
exit(exitCode);
}
}
int main(int argc, char **argv) {
Display *dpy = XOpenDisplay(0);
exitOnCondition(dpy == 0, "Error: XOpenDisplay failed", -1, dpy, 0, 0);
int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, blackColor, blackColor);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
//With modifier XLookupString will return garbage(?) in text[0] and key as latin1
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
printf("lookup returned:\n");
for(int i = 0; i < numKeys; i++) {
printf("text[%d]=%x\n", i, text[i]);
}
if(e.xkey.state == ControlMask && key == XK_q) {
exitOnCondition(1, "C-Q pressed", 0, dpy, &w, &gc);
}
}
}
}
XFreeGC(dpy, gc);
XDestroyWindow(dpy,w);
XCloseDisplay(dpy);
}
Will this code correctly handle Ctrl-q event on any system?
Can I use e.xkey.state to check for Ctrl modifier after XLookupString for any keyboard layout even when Ctrl is rebound to CAPS Lock(or anything else)?
Why does XLookupString return one symbol text[0]==0x11 for Ctrl-q event and not text[0]==CtrlModifierCode text[1]=='q'?
XLookupString returns a sequence of ISO-8859-1 characters (at least according to the manual I have; I do not know how up-to-date it is). There is no character code in ISO-8859-1 for the "ctrl" key by itself. Strictly speaking, there isn't one for the ctrl-q combination either, but tradition dictates that ctrl + (A...Z) map to the 1...26 range.