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.