XCB: window will not unmap after being mapped once - c

I have a small example program written in C that opens a window using the XCB API.
Strictly AFTER I have created and shown the window, I would (at a later time) like to hide the window.
(Obviously in this specific example, I could remove the call to xcb_map_window, and the window would be hidden, but I want to do it at a later point in my larger application, like a toggle to show/hide the window, NOTE: I do NOT want to minimize it).
Here is the sample code (NOTE: this code now works thanks to the answer):
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <xcb/xcb.h>
void set_window_visible(xcb_connection_t* c, xcb_window_t win, bool visible) {
xcb_generic_event_t *event;
if(visible) {
// Map the window on the screen
xcb_map_window (c, win);
// Make sure the map window command is sent
xcb_flush(c);
// Wait for EXPOSE event.
//
// TODO: add timeout in-case X server does not ever send the expose event.
while(event = xcb_wait_for_event(c)) {
bool gotExpose = false;
switch(event->response_type & ~0x80) {
case XCB_EXPOSE:
gotExpose = true;
break;
default:
break; // We don't know the event type, then.
}
free(event);
if(gotExpose) {
break;
}
}
} else {
// Hide the window
xcb_unmap_window(c, win);
// Make sure the unmap window command is sent
xcb_flush(c);
}
}
int main() {
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_generic_event_t *event;
// Open the connection to the X server
c = xcb_connect (NULL, NULL);
// Get the first screen
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
// Ask for our window's Id
win = xcb_generate_id(c);
// Create the window
uint32_t mask = XCB_CW_EVENT_MASK;
uint32_t valwin[] = {XCB_EVENT_MASK_EXPOSURE | XCB_BUTTON_PRESS};
xcb_create_window(
c, // Connection
XCB_COPY_FROM_PARENT, // depth (same as root)
win, // window Id
screen->root, // parent window
0, 0, // x, y
150, 150, // width, height
10, // border_width
XCB_WINDOW_CLASS_INPUT_OUTPUT, // class
screen->root_visual, // visual
mask, valwin // masks
);
bool visible = true;
set_window_visible(c, win, true);
while(1) {
sleep(2);
// Toggle visibility
visible = !visible;
set_window_visible(c, win, visible);
printf("Window visible: ");
if(visible) {
printf("true.\n");
} else {
printf("false.\n");
}
}
// pause until Ctrl-C
pause();
return 0;
}
Which I compile and run with:
gcc xcbwindow.c -o xcbwindow -lxcb
./xcbwindow
From anything I can find on Google or here, I am doing everything correctly. So for clarification I am using Unity and Ubuntu 12.04 LTS:
unity --version reports:
unity 5.20.0
uname -a reports:
Linux [redacted] 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
Can anyone explain where I've gone wrong in this code?
EDIT: updated code with a flush() at the end after xcb_unmap_window(); still doesn't work.
EDIT2: Tried code with cinnamon WM; still doesn't work (It's not a Unity bug).
EDIT3: Code updated in this post now works.

Your program simply goes too fast.
It maps the window and then immediately unmaps it. The window is top level, which means the requests are redirected to the window manager. But the window manager receives the unmap request when the window is not mapped yet, so it simply discards the request. Insert sleep(3) between the map and unmap calls and observe.
In real code, your window needs to get at least one expose event before sending out the unmap request. This guarantees it's actually mapped by the window manager.

Related

Cant fin definition of XMapWindow() in Xlib headers

So i have this simple code that draws a small window using X11
int main(int, char*[])
{
Display* display = XOpenDisplay(NULL);
Window window = XCreateSimpleWindow(
display, XDefaultRootWindow(display),
100, 100, 200, 200, 4, 0, 0);
XEvent event;
XMapWindow(display, window);
XSelectInput(display, window, KeyPressMask | ButtonPressMask | ExposureMask);
while (True) {
XNextEvent(display, &event);
}
return 0;
}
Everything works fine, the window its drawed without problems.
The thing is i really want to understand how X11 works so im reading the source code of the headers but im unable to find the difinition for XMapWindow() i need help.
This was the only similutede i could find in the Xlib.h header file.
extern int XMapWindow(
Display* /* display */,
Window /* w */
);
You can find the implementation of XMapWindow() in the libX11 sources, specifically in src/MapWindow.c:XMapWindow().
It boils down to some locking, and a _XGetRequest(dpy, X_MapWindow, SIZEOF(xResourceReq)) call. That is defined in src/XlibInt.c:_XGetRequest(), and as you can guess by the name, adds the X_MapWindow request to the request queue sent to the X server.
How the X server (which manages the actual display) acts on that, is up to that X server implementation.

How do I Monitor Mouse Movement Events in All Windows (Not Just One) on X11

I'm trying write an X11 program to monitor all mouse movements on the desktop. The program should be able to receive a notification whenever the mouse is moved by the human user, or moved programmatically via XWarpPointer() by a robotic application. I know it should be possible by setting a PointerMotionMask via XSelectInput() and monitor MotionNotify, but I'm having troubles receiving mouse events from all windows, not just one.
Initially, I just tried to receive pointer motion events from the root window, in the following demo.
#include <stdio.h>
#include <X11/Xlib.h>
int main(int argc, char **argv)
{
Display *display;
Window root_window;
XEvent event;
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
XSelectInput(display, root_window, PointerMotionMask);
while (1) {
XNextEvent(display, &event);
switch(event.type) {
case MotionNotify:
printf("x: %d y: %d\n", event.xmotion.x, event.xmotion.y );
break;
}
}
return 0;
}
But it doesn't receive any events, unless the mouse pointer is on an empty desktop background. It's clear that merely receiving events from the root window won't work. Then I tried a workaround: first, set SubstructureNotifyMask on the root window to monitor all CreateNotify events to catch all newly created windows, then call XSelectInput() to enable the PointerMotionMask on these windows.
#include <stdio.h>
#include <X11/Xlib.h>
int main(int argc, char **argv)
{
Display *display;
Window root_window;
XEvent event;
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
XSelectInput(display, root_window, SubstructureNotifyMask);
while (1) {
XNextEvent(display, &event);
switch(event.type) {
case CreateNotify:
XSelectInput(display, event.xcreatewindow.window, PointerMotionMask);
break;
case MotionNotify:
printf("x: %d y: %d\n", event.xmotion.x, event.xmotion.y);
break;
}
}
return 0;
}
This approach is more successful, I started to receive some mouse events from new windows. Unfortunately, it still doesn't work in all portions inside a window - for example, it cannot receive mouse events from the console area in terminal emulators, but can receive events when the mouse is located around the title bar. It appears that a window can create more subwindows, so the mouse events won't be recorded.
Then I tried another workaround - set both SubstructureNotifyMask and PointerMotionMask in CreateNotify, so when a window creates a child window, SubstructureNotifyMask ensures more CreateNotify events will be received in a recursive manner, so all child windows will get PointerMotionMask as well.
#include <stdio.h>
#include <X11/Xlib.h>
int main(int argc, char **argv)
{
Display *display;
Window root_window;
XEvent event;
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
XSelectInput(display, root_window, SubstructureNotifyMask);
while (1) {
XNextEvent(display, &event);
switch(event.type) {
case CreateNotify:
XSelectInput(display, event.xcreatewindow.window, SubstructureNotifyMask | PointerMotionMask);
break;
case MotionNotify:
printf("x: %d y: %d\n", event.xmotion.x, event.xmotion.y);
break;
}
}
return 0;
}
It works a bit better than the second example, but it's not reliable:
X is fully asynchronous, is it possible that the child window got created before we had a chance to XSelectInput()?
Sometimes it just reports a BadWindow error and crashes.
X event handling becomes messy - if the program already handles a lot of different X events, enabling SubstructureNotifyMask recursively will make many unrelated events delivered to other handlers, and it's a pain to add extra code to discriminate between wanted and unwanted events.
So, how do I monitor mouse movement events in all windows on X11?
After doing some research, especially reading the source code of Xeyes (I always fell the demo is stupid, but it helps a lot here!), I found:
Calling XSelectInput() on all the windows and subwindows is a futile attempt, you have to set a mask on every single window and child window ever created, it's not a robust solution, and not recommended.
Instead, it's better to just continuously pulling the mouse pointer from the X server explicitly via XQueryPointer(), rather than asking the X server to push MotionEvent to us.
One naive solution is simply setting up a timer by XtAppAddTimeOut() and calling XQueryPointer() periodically, it works, and indeed, it was what Xeyes did in the past! But it unnecessarily wastes CPU time. Nowadays, the best practice is to take advantage of XInputExtention 2.0. The workflow is:
Initialize XInput v2.0
Enable various masks via XISetMask() and XIEventMask() to receive XI_RawMotion events (or XI_Motion, see notes below) from XIAllMasterDevices (or XIAllDevices).
When a XI_RawMotion (or XI_Motion) event has been received, call XQueryPointer().
XQueryPointer() returns:
Mouse coordinates with respect to the root window.
The active window under the mouse cursor, if any.
Perform a XTranslateCoordinates() if we want relative coordinates with respect to the active window under the mouse cursor.
Demo
Here's a demo (save as mouse.c, compile with gcc mouse.c -o mouse -lX11 -lXi). However, it cannot detect XWarpPointer(), see notes below.
#include <stdio.h>
#include <assert.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
int main(int argc, char **argv)
{
Display *display;
Window root_window;
/* Initialize (FIXME: no error checking). */
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
/* check XInput */
int xi_opcode, event, error;
if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) {
fprintf(stderr, "Error: XInput extension is not supported!\n");
return 1;
}
/* Check XInput 2.0 */
int major = 2;
int minor = 0;
int retval = XIQueryVersion(display, &major, &minor);
if (retval != Success) {
fprintf(stderr, "Error: XInput 2.0 is not supported (ancient X11?)\n");
return 1;
}
/*
* Set mask to receive XI_RawMotion events. Because it's raw,
* XWarpPointer() events are not included, you can use XI_Motion
* instead.
*/
unsigned char mask_bytes[(XI_LASTEVENT + 7) / 8] = {0}; /* must be zeroed! */
XISetMask(mask_bytes, XI_RawMotion);
/* Set mask to receive events from all master devices */
XIEventMask evmasks[1];
/* You can use XIAllDevices for XWarpPointer() */
evmasks[0].deviceid = XIAllMasterDevices;
evmasks[0].mask_len = sizeof(mask_bytes);
evmasks[0].mask = mask_bytes;
XISelectEvents(display, root_window, evmasks, 1);
XEvent xevent;
while (1) {
XNextEvent(display, &xevent);
if (xevent.xcookie.type != GenericEvent || xevent.xcookie.extension != xi_opcode) {
/* not an XInput event */
continue;
}
XGetEventData(display, &xevent.xcookie);
if (xevent.xcookie.evtype != XI_RawMotion) {
/*
* Not an XI_RawMotion event (you may want to detect
* XI_Motion as well, see comments above).
*/
XFreeEventData(display, &xevent.xcookie);
continue;
}
XFreeEventData(display, &xevent.xcookie);
Window root_return, child_return;
int root_x_return, root_y_return;
int win_x_return, win_y_return;
unsigned int mask_return;
/*
* We need:
* child_return - the active window under the cursor
* win_{x,y}_return - pointer coordinate with respect to root window
*/
int retval = XQueryPointer(display, root_window, &root_return, &child_return,
&root_x_return, &root_y_return,
&win_x_return, &win_y_return,
&mask_return);
if (!retval) {
/* pointer is not in the same screen, ignore */
continue;
}
/* We used root window as its reference, so both should be the same */
assert(root_x_return == win_x_return);
assert(root_y_return == win_y_return);
printf("root: x %d y %d\n", root_x_return, root_y_return);
if (child_return) {
int local_x, local_y;
XTranslateCoordinates(display, root_window, child_return,
root_x_return, root_y_return,
&local_x, &local_y, &child_return);
printf("local: x %d y %d\n\n", local_x, local_y);
}
}
XCloseDisplay(display);
return 0;
}
Sample Output
root: x 631 y 334
local: x 140 y 251
root: x 628 y 338
local: x 137 y 255
root: x 619 y 343
local: x 128 y 260
XWarpPointer() Troubles
The demo above doesn't work if the pointer is moved via XWarpPointer() by a robotic application on newer systems after X.Org 1.10.4. This is intentional, see Bug 30068 on FreeDesktop.
In order to receive mouse events triggered by all mouse movements, including XWarpPointer(), change XI_RawMotion to XI_Motion, and change XIAllMasterDevices to XIAllDevices.
References
This demo lacks error checking and may contain bugs. If in doubts, please check the following authoritative references.
Tracking Cursor Position by Keith Packard, a real X expert, has been heavily involved in the development of X since the late 1980s, and responsible for many X extensions and technical papers.
Xeyes source code from X.Org.

Why XServer sends FocusOut notify twice

I write dummy window for software KVM. Idea is when user switches to another machine, previous machine opens dummy window beyond the screen bounds, so emulates focus lost. Windows without focus are transparent on my system (awesome wm). If I just use XSetInputFocus(dpy, None, RevertToNone, CurrentTime) current window loses focus but doesn't become transparent. That is why I use dummy window.
Come to the point. My program saves focused window before opens dummy window. When dummy window loses focus program restores saved. So It needs to catch FocusOut event and then call XSetInputFocus function to restore focus. Problem is that xserver sends FocusOut notify twice. If I handle it only once program works incorrect.
Code is below.
#include <X11/Xlib.h>
#include <X11/Xatom.h>
int x = 100, y = 100, height = 200, width = 200;
typedef struct {
Window win;
int notify;
} wait_arg;
Bool xevent_handler(Display *dpy, XEvent *ev, XPointer arg) {
wait_arg *a = (wait_arg *) arg;
return (ev->type == a->notify) && (ev->xvisibility.window == a->win);
}
int main(int argc, char *argv[]) {
Display *dpy;
Window focused;
int revert_to;
Window win;
wait_arg arg;
XEvent ev;
dpy = XOpenDisplay(NULL);
// Save window which has focus now
XGetInputFocus(dpy, &focused, &revert_to);
int s = DefaultScreen(dpy);
win = XCreateSimpleWindow(dpy, RootWindow(dpy, s), 0, 0, height, width, 0,
CopyFromParent, CopyFromParent);
// Set DIALOG type for window
Atom type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
long value = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
XChangeProperty(dpy, win, type, XA_ATOM, 32, PropModeReplace,
(unsigned char *) &value, 1);
// Set events to handle
XSelectInput(dpy, win, VisibilityChangeMask | FocusChangeMask);
// Draw window
XMapWindow(dpy, win);
// Wait until window stands visible, otherwise will get
// "Error of failed request: BadMatch"
arg.win = win;
arg.notify = VisibilityNotify;
XIfEvent(dpy, &ev, &xevent_handler, (XPointer) &arg);
XSetInputFocus(dpy, win, RevertToNone, CurrentTime);
XSync(dpy, False);
XMoveWindow(dpy, win, x, y);
// Wait until focus lost
arg.win = win;
arg.notify = FocusOut;
XIfEvent(dpy, &ev, &xevent_handler, (XPointer) &arg);
// Why I should handle this event twice?
XIfEvent(dpy, &ev, &xevent_handler, (XPointer) &arg);
// Restore focus
XSetInputFocus(dpy, focused, revert_to, CurrentTime);
XSync(dpy, False);
}
How to check incorrect behavior:
I comment second FocusOut handling. I use two monitors: left and right. I start program in terminal on right monitor. Program opens dummy window on left monitor. When dummy window loses focus another window on left monitor obtains it. Not terminal window on right monitor!
Why xserver sends FocusOut twice? Or it is bug/feature of my window manager?
Without your implementation it is really impossible to know exactly what the driver is for the behavior you are seeing, but the man pages do provide some insight into what may be happening.
This particular behavior may be the result of the last-focus-change time. From this man page:
...The XSetInputFocus() function changes the input focus and the last-focus-change time. It has no effect if the specified time is earlier than the current
last-focus-change time or is later than the current X server time.

Simple C Program that creates 2 X11 windows

I want to create 2 windows in linux that I'll later draw in from a separate thread. I currently have a non-deterministic bug where the second window that I create sometimes doesn't get created (no errors though).
Here is the code.
static void create_x_window(Display *display, Window *win, int width, int height)
{
int screen_num = DefaultScreen(display);
unsigned long background = WhitePixel(display, screen_num);
unsigned long border = BlackPixel(display, screen_num);
*win = XCreateSimpleWindow(display, DefaultRootWindow(display), /* display, parent */
0,0, /* x, y */
width, height, /* width, height */
2, border, /* border width & colour */
background); /* background colour */
XSelectInput(display, *win, ButtonPressMask|StructureNotifyMask);
XMapWindow(display, *win);
}
int main(void) {
XInitThreads(); // prevent threaded XIO errors
local_display = XOpenDisplay(":0.0");
Window self_win, remote_win;
XEvent self_event, remote_event;
create_x_window(local_display, &remote_win, 640,480);
// this line flushes buffer and blocks so that the window doesn't crash for a reason i dont know yet
XNextEvent(local_display, &remote_event);
create_x_window(local_display, &self_win, 320, 240);
// this line flushes buffer and blocks so that the window doesn't crash for a reason i dont know yet
XNextEvent(local_display, &self_event);
while (1) {
}
return 0;
}
I don't really care for capturing input in the windows, but I found a tutorial that had XSelectInput and XNextEvent (in an event loop) and I was having trouble making this work without either.
It's not a bug, it's a feature. You left out the event loop.
Although you cleverly called XNextEvent twice, the X protocol is asynchronous so the server may still be setting up the actual window while you call XNextEvent, so there is nothing to do.
Tutorial here.

Get a screenshot of a window that is cover or not visible or minimized with Xcomposite extension for X11

I have the follow starting code to get a screenshot of a X window (the window can be covered, not visibled, or minimized).
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/X.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrender.h>
int
main ()
{
Display *display = XOpenDisplay (NULL);
XID xid = 90177543; // xdotool search --name "World of Warcraft" | head -1
// Check if Composite extension is enabled
int event_base_return;
int error_base_return;
if (XCompositeQueryExtension (display, &event_base_return, &error_base_return))
printf ("COMPOSITE IS ENABLED!\n");
// Requests the X server to direct the hierarchy starting at window to off-screen storage
XCompositeRedirectWindow (display, xid, CompositeRedirectAutomatic);
// Preventing the backing pixmap from being freed when the window is hidden/destroyed
// If you want the window contents to still be available after the window has been destroyed,
// or after the window has been resized (but not yet redrawn), you can increment the backing
// pixmaps ref count to prevent it from being deallocated.
Pixmap pixmap = XCompositeNameWindowPixmap (display, xid);
// Get window attributes
XWindowAttributes attr;
Status s = XGetWindowAttributes (display, xid, &attr);
if (s == 0)
printf ("Fail to get window attributes!\n");
// Extract the data
XRenderPictFormat *format = XRenderFindVisualFormat (display, attr.visual);
int width = attr.width;
int height = attr.height;
int depth = attr.depth;
// What we need to do now is to create an XRender Picture for the window,
// which we'll need to draw it with the Render extension.
// A picture is a basically a handle to a server side struct with some
// additional information about a drawable (in this case a window),
// such as its format, which clipping region should be used when
// drawing it (if any), whether it should be tiled etc.
XRenderPictureAttributes pa;
pa.subwindow_mode = IncludeInferiors;
Picture picture = XRenderCreatePicture (display, xid, format, CPSubwindowMode, &pa);
// We now have all the information we need in order to be able to draw the window
// using the Xrender extension, and we've created and prepared a source picture
// for the window for this purpose.
// The Xrender function we'll use to draw the window is XRenderComposite().
//XRenderComposite (display, PictOpSrc, picture, None, ???destination???, 0,0, 0,0, 0,0, width, height);
XFreePixmap (display, pixmap);
XCompositeUnredirectWindow (display, xid, CompositeRedirectAutomatic);
return 0;
}
[Compile the code with gcc file.c -lX11 -lXcomposite -lXrender and run with ./a.out]
[You can get a valid XID of a window of your Desktop with the command xdotool search --name "Title of a window" | head -1]
Now I have two problem:
I get a BadMatch (invalid parameter attributes) error when I call the XRenderFindVisualFormat function. I dont know why. The window with XID 90177543 exists.
I don't know how to get the buffer of the Picture picture to save it as a PNG image. I don't want use QT library to do this.
Can you help me?
I am able to reproduce your error as follows:
$ ./xidtest
COMPOSITE IS ENABLED!
X Error of failed request: BadWindow (invalid Window parameter)
Major opcode of failed request: 142 (Composite)
Minor opcode of failed request: 1 ()
Resource id in failed request: 0x5600007
Serial number of failed request: 9
Current serial number in output stream: 12
When set using a window id I knew to exist (0x440000B or 71303179, found using xwininfo), I received no error:
$ ./xidtest
COMPOSITE IS ENABLED!
I conclude that the error is the window id you have provided is invalid.

Resources