How to Get Started Writing a Compositing Wm

How to get started writing a compositing WM?

There are two parts in your question: 1) How to write WM 2) how to write composite manager

Some links to help understand part two (in addition to xcompmgr source):

  • http://www.talisman.org/~erlkonig/misc/x11-composite-tutorial/ (Uses Qt, but very generic and low level)
  • https://github.com/gustavosbarreto/compmgr
  • http://projects.mini-dweeb.org/projects/unagi

Window manager, "part one":

  • I have simple ~100 loc wm in JavaScript: https://github.com/sidorares/node-x11/blob/master/examples/windowmanager/wm.js
  • another minimalist wm (in C), good to start as a reference: https://code.google.com/p/winmalist/
  • most important keyword: SubstructureRedirect event mask. A bit of documentation here

Request image from X11 compositing WM in C or C++

I now have a working example that I will post here:

#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include <xcb/composite.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s windowId\n", argv[0]);
return EXIT_FAILURE;
}
xcb_window_t req_win_id = strtoul(argv[1], NULL, 0);
xcb_connection_t *connection = xcb_connect(NULL, NULL);
xcb_generic_error_t *err = NULL, *err2 = NULL;

xcb_composite_query_version_cookie_t comp_ver_cookie = xcb_composite_query_version(connection, 0, 2);
xcb_composite_query_version_reply_t *comp_ver_reply = xcb_composite_query_version_reply(connection, comp_ver_cookie, &err);
if (comp_ver_reply)
{
if (comp_ver_reply->minor_version < 2) {
fprintf(stderr, "query composite failure: server returned v%d.%d\n", comp_ver_reply->major_version, comp_ver_reply->minor_version);
free(comp_ver_reply);
return EXIT_FAILURE;
}
free(comp_ver_reply);
}
else if (err)
{
fprintf(stderr, "xcb error: %d\n", err->error_code);
free(err);
return EXIT_FAILURE;
}

const xcb_setup_t *setup = xcb_get_setup(connection);
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
xcb_screen_t *screen = screen_iter.data;
// request redirection of window
xcb_composite_redirect_window(connection, req_win_id, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
int win_h, win_w, win_d;

xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(connection, req_win_id);
xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(connection, gg_cookie, &err);
if (gg_reply) {
win_w = gg_reply->width;
win_h = gg_reply->height;
win_d = gg_reply->depth;
free(gg_reply);
} else {
if (err) {
fprintf(stderr, "get geometry: XCB error %d\n", err->error_code);
free(err);
}
return EXIT_FAILURE;
}

// create a pixmap
xcb_pixmap_t win_pixmap = xcb_generate_id(connection);
xcb_composite_name_window_pixmap(connection, req_win_id, win_pixmap);

// get the image
xcb_get_image_cookie_t gi_cookie = xcb_get_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, win_pixmap, 0, 0, win_w, win_h, (uint32_t)(~0UL));
xcb_get_image_reply_t *gi_reply = xcb_get_image_reply(connection, gi_cookie, &err);
if (gi_reply) {
int data_len = xcb_get_image_data_length(gi_reply);
fprintf(stderr, "data_len = %d\n", data_len);
fprintf(stderr, "visual = %u\n", gi_reply->visual);
fprintf(stderr, "depth = %u\n", gi_reply->depth);
fprintf(stderr, "size = %dx%d\n", win_w, win_h);
uint8_t *data = xcb_get_image_data(gi_reply);
fwrite(data, data_len, 1, stdout);
free(gi_reply);
}
return EXIT_SUCCESS;
}

It was a case of overdoing it. I got the idea that I should use Xrender from somewhere and started pulling tons of data from that extension that I didn't really need. But the real problem I had was that the pixmap given to xcb_composite_name_window_pixmap should not be created first, but rather I just needed the new id for it. It turns out that it is really as simple as I expected it to be. All you need is the Window ID and the size, do some checks to make sure composite is there and a good version (>=0.2 in this case), and call xcb_composite_redirect_window, xcb_composite_name_window_pixmap (with a generated pixmap id), and xcb_get_image (+reply,data_len,data) in that order, and you have the image.

How does compositor work on X?

The simpler you code it, the best it will work.

  • Get a list of the visible windows
  • Sort them by inverse z-order (from the bottom-most to the top-most)
  • Draw the shadow and then the window itself for each window

You need no black magic.

If you are wondering how it works, it's straightforward: you have to use the 'composite' X extension. It enables the overlay window, which is the only visible window on the screen once it is enabled, then you have to draw all the windows on it as you will be provided with a Pixmap for each window.

EDIT:
If you are seeking for documentation, you can use the linux manual (the man command), and the header files, they're the main (also best and perhaps the only real) source of documentation, as all the other sources/websites rely on them afaik.

How to make an overlay of all tags

My collision module or the bling module has something close enough. Getting the exact layout you ask for is non-trivial because AwesomeWM is not a compositing window manager. This means it cannot really take screenshots (let alone live-views) of invisible clients/windows. Usually, the only "safe" thing is to display the outline and client icon.

If you really, really want this, you need:

  1. A compositing manager such as picom
  2. Either these patches or use gears.surface(client.content) to take a screenshot
  3. Lot of code to properly render a wibox with the right screenshots. You can read the bling or collision code to know how to get the size and position.


Related Topics



Leave a reply



Submit