From a5cf7cabf38e2e894cfd7df92f521dbd8218f359 Mon Sep 17 00:00:00 2001 From: hkc Date: Thu, 6 Jun 2024 14:48:57 +0300 Subject: [PATCH] Trying to list all windows from _NET_CLIENT_LIST --- img/scrall/.gitignore | 1 + img/scrall/Makefile | 8 +- img/scrall/main.c | 85 ++++---------- img/scrall/screenshot.c | 58 ++++++++++ img/scrall/screenshot.h | 9 ++ img/scrall/state | 238 ++++++++++++++++++++++++++++++++++++++++ img/scrall/windowtree.c | 130 ++++++++++++++++++++++ img/scrall/windowtree.h | 19 ++++ 8 files changed, 480 insertions(+), 68 deletions(-) create mode 100644 img/scrall/screenshot.c create mode 100644 img/scrall/screenshot.h create mode 100644 img/scrall/state create mode 100644 img/scrall/windowtree.c create mode 100644 img/scrall/windowtree.h diff --git a/img/scrall/.gitignore b/img/scrall/.gitignore index 31adcbb..bea8e18 100644 --- a/img/scrall/.gitignore +++ b/img/scrall/.gitignore @@ -1 +1,2 @@ scrall +*.o diff --git a/img/scrall/Makefile b/img/scrall/Makefile index 7aa5402..a798906 100644 --- a/img/scrall/Makefile +++ b/img/scrall/Makefile @@ -1,9 +1,13 @@ CFLAGS += -Wall -Wextra `exec pkg-config --cflags raylib xcb xcb-composite` LDFLAGS := -lm `pkg-config --libs raylib xcb xcb-composite` -scrall: main.c - $(CC) $(CFLAGS) main.c $(LDFLAGS) -o scrall +OBJECTS = ./screenshot.o ./windowtree.o +scrall: main.c $(OBJECTS) + $(CC) $(CFLAGS) main.c $(LDFLAGS) -o scrall $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $^ -o $@ clean: $(RM) scrall diff --git a/img/scrall/main.c b/img/scrall/main.c index 9a254fd..420d1bc 100644 --- a/img/scrall/main.c +++ b/img/scrall/main.c @@ -1,85 +1,38 @@ // x-run: make run -#include #include #include -#include #include #include -#include +#include "screenshot.h" +#include "windowtree.h" -Image get_screenshot(xcb_connection_t *conn, xcb_window_t wid); - int main(int argc, char **argv) { + (void)argc; + (void)argv; xcb_connection_t *xcb = xcb_connect(NULL, NULL); + xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data; + + printf("root: 0x%08x\n", screen->root); + + size_t n_windows; + struct window_info *windows = get_windows_list(xcb, &n_windows); + + for (int i = 0; i < n_windows; i++) { + printf("0x%08x %02x %s\n", windows[i].wid, windows[i].win_gravity, windows[i].title); + } + + xcb_disconnect(xcb); + + return 0; + SetConfigFlags(FLAG_WINDOW_TRANSPARENT); InitWindow(0, 0, "img/scrall"); - - Image screenshot = get_screenshot(xcb, 50331654); - assert(screenshot.width > 0); - - Texture2D screenshot_tex = LoadTextureFromImage(screenshot); - SetWindowSize(screenshot.width, screenshot.height); - while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(BLANK); - DrawTexture(screenshot_tex, 0, 0, WHITE); EndDrawing(); } } - -Image get_screenshot(xcb_connection_t *conn, xcb_window_t wid) { - xcb_generic_error_t *err = NULL; - - xcb_composite_redirect_window(conn, wid, XCB_COMPOSITE_REDIRECT_AUTOMATIC); - - xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(conn, wid); - xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(conn, gg_cookie, &err); - - if (!gg_reply) { - fprintf(stderr, "xcb: error: geometry: %d\n", err->error_code); - return (Image){ - .data = err, .width = 0, .height = 0, .format = 0, .mipmaps = 0 - }; - } - - int win_w = gg_reply->width; - int win_h = gg_reply->height; - assert(gg_reply->depth == 32); - fprintf(stderr, "width: %d\nheight: %d\n", win_w, win_h); - - free(gg_reply); - - xcb_pixmap_t win_pixmap = xcb_generate_id(conn); - xcb_composite_name_window_pixmap(conn, wid, win_pixmap); - - xcb_get_image_cookie_t gi_cookie = xcb_get_image(conn, 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(conn, gi_cookie, &err); - if (!gi_reply) { - fprintf(stderr, "xcb: error: get_image_reply: %d\n", err->error_code); - return (Image){ - .data = err, .width = 0, .height = 0, .format = 0, .mipmaps = 0 - }; - } - - int data_len = xcb_get_image_data_length(gi_reply); - fprintf(stderr, "data_len: %d\n", data_len); - - uint8_t *data = xcb_get_image_data(gi_reply); - - Image img = GenImageColor(win_w, win_h, BLANK); - - for (int i = 0; i < data_len; i += 4) { - ((uint8_t *)img.data)[i + 0] = data[i + 2]; - ((uint8_t *)img.data)[i + 1] = data[i + 1]; - ((uint8_t *)img.data)[i + 2] = data[i + 0]; - ((uint8_t *)img.data)[i + 3] = data[i + 3]; - } - - free(gi_reply); - - return img; -} diff --git a/img/scrall/screenshot.c b/img/scrall/screenshot.c new file mode 100644 index 0000000..d848638 --- /dev/null +++ b/img/scrall/screenshot.c @@ -0,0 +1,58 @@ +#include "screenshot.h" +#include +#include +#include +#include + +Image get_screenshot(xcb_connection_t *conn, xcb_window_t wid) { + xcb_generic_error_t *err = NULL; + + xcb_composite_redirect_window(conn, wid, XCB_COMPOSITE_REDIRECT_AUTOMATIC); + + xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(conn, wid); + xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(conn, gg_cookie, &err); + + if (!gg_reply) { + fprintf(stderr, "xcb: error: geometry: %d\n", err->error_code); + return (Image){ + .data = err, .width = 0, .height = 0, .format = 0, .mipmaps = 0 + }; + } + + int win_w = gg_reply->width; + int win_h = gg_reply->height; + assert(gg_reply->depth == 32); + fprintf(stderr, "width: %d\nheight: %d\n", win_w, win_h); + + free(gg_reply); + + xcb_pixmap_t win_pixmap = xcb_generate_id(conn); + xcb_composite_name_window_pixmap(conn, wid, win_pixmap); + + xcb_get_image_cookie_t gi_cookie = xcb_get_image(conn, 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(conn, gi_cookie, &err); + if (!gi_reply) { + fprintf(stderr, "xcb: error: get_image_reply: %d\n", err->error_code); + return (Image){ + .data = err, .width = 0, .height = 0, .format = 0, .mipmaps = 0 + }; + } + + int data_len = xcb_get_image_data_length(gi_reply); + fprintf(stderr, "data_len: %d\n", data_len); + + uint8_t *data = xcb_get_image_data(gi_reply); + + Image img = GenImageColor(win_w, win_h, BLANK); + + for (int i = 0; i < data_len; i += 4) { + ((uint8_t *)img.data)[i + 0] = data[i + 2]; + ((uint8_t *)img.data)[i + 1] = data[i + 1]; + ((uint8_t *)img.data)[i + 2] = data[i + 0]; + ((uint8_t *)img.data)[i + 3] = data[i + 3]; + } + + free(gi_reply); + + return img; +} diff --git a/img/scrall/screenshot.h b/img/scrall/screenshot.h new file mode 100644 index 0000000..0e0d0e9 --- /dev/null +++ b/img/scrall/screenshot.h @@ -0,0 +1,9 @@ +#ifndef _SCREENSHOT_H_ +#define _SCREENSHOT_H_ + +#include +#include + +Image get_screenshot(xcb_connection_t *conn, xcb_window_t wid); + +#endif diff --git a/img/scrall/state b/img/scrall/state new file mode 100644 index 0000000..339d8ec --- /dev/null +++ b/img/scrall/state @@ -0,0 +1,238 @@ +let SessionLoad = 1 +let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1 +let v:this_session=expand(":p") +silent only +silent tabonly +cd ~/projects/things/img/scrall +if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == '' + let s:wipebuf = bufnr('%') +endif +let s:shortmess_save = &shortmess +if &shortmess =~ 'A' + set shortmess=aoOA +else + set shortmess=aoO +endif +badd +15 main.c +badd +61 ~/projects/experiments/composegrab.c +badd +4 Makefile +badd +1 term://~/projects/things/img/scrall//871627:/bin/bash +badd +2 screenshot.c +badd +9 screenshot.h +badd +40 term://~/projects/things/img/scrall//1272817:elinks +badd +66 ~/projects/livewp/src/rootwindow.c +badd +1 term://elinks\ https://stackoverflow.com/questions/71947949/how-to-get-top-level-windows-and-their-names-using-libxcb +badd +1 term://\'elinks\ https://stackoverflow.com/questions/71947949/how-to-get-top-level-windows-and-their-names-using-libxcb\' +badd +1 term://sh\ -c\ \'elinks\ https://stackoverflow.com/questions/71947949/how-to-get-top-level-windows-and-their-names-using-libxcb\' +badd +1 term://~/projects/things/img/scrall//1274970:elinks +badd +15 windowtree.h +badd +0 man://xcb_get_property_value +badd +2 windowtree.c +badd +372 /usr/include/xcb/xcb.h +badd +1 /usr/include/xcb/xcb_atom.h +badd +1 /usr/include/xcb/xcb_util.h +badd +2 /usr/include/xcb/xcb_aux.h +badd +0 /usr/include/xcb/xproto.h +argglobal +%argdel +$argadd main.c +edit main.c +let s:save_splitbelow = &splitbelow +let s:save_splitright = &splitright +set splitbelow splitright +wincmd _ | wincmd | +vsplit +1wincmd h +wincmd _ | wincmd | +split +1wincmd k +wincmd _ | wincmd | +vsplit +1wincmd h +wincmd w +wincmd w +wincmd _ | wincmd | +vsplit +1wincmd h +wincmd w +wincmd w +wincmd _ | wincmd | +split +1wincmd k +wincmd w +let &splitbelow = s:save_splitbelow +let &splitright = s:save_splitright +wincmd t +let s:save_winminheight = &winminheight +let s:save_winminwidth = &winminwidth +set winminheight=0 +set winheight=1 +set winminwidth=0 +set winwidth=1 +exe '1resize ' . ((&lines * 42 + 43) / 86) +exe 'vert 1resize ' . ((&columns * 106 + 159) / 319) +exe '2resize ' . ((&lines * 42 + 43) / 86) +exe 'vert 2resize ' . ((&columns * 106 + 159) / 319) +exe '3resize ' . ((&lines * 41 + 43) / 86) +exe 'vert 3resize ' . ((&columns * 106 + 159) / 319) +exe '4resize ' . ((&lines * 41 + 43) / 86) +exe 'vert 4resize ' . ((&columns * 106 + 159) / 319) +exe '5resize ' . ((&lines * 42 + 43) / 86) +exe 'vert 5resize ' . ((&columns * 105 + 159) / 319) +exe '6resize ' . ((&lines * 41 + 43) / 86) +exe 'vert 6resize ' . ((&columns * 105 + 159) / 319) +argglobal +balt term://~/projects/things/img/scrall//871627:/bin/bash +setlocal fdm=manual +setlocal fde=0 +setlocal fmr={{{,}}} +setlocal fdi=# +setlocal fdl=0 +setlocal fml=1 +setlocal fdn=20 +setlocal fen +silent! normal! zE +let &fdl = &fdl +let s:l = 15 - ((14 * winheight(0) + 21) / 42) +if s:l < 1 | let s:l = 1 | endif +keepjumps exe s:l +normal! zt +keepjumps 15 +normal! 07| +wincmd w +argglobal +if bufexists(fnamemodify("/usr/include/xcb/xproto.h", ":p")) | buffer /usr/include/xcb/xproto.h | else | edit /usr/include/xcb/xproto.h | endif +if &buftype ==# 'terminal' + silent file /usr/include/xcb/xproto.h +endif +balt /usr/include/xcb/xcb_aux.h +setlocal fdm=manual +setlocal fde=0 +setlocal fmr={{{,}}} +setlocal fdi=# +setlocal fdl=0 +setlocal fml=1 +setlocal fdn=20 +setlocal fen +silent! normal! zE +let &fdl = &fdl +let s:l = 2000 - ((30 * winheight(0) + 21) / 42) +if s:l < 1 | let s:l = 1 | endif +keepjumps exe s:l +normal! zt +keepjumps 2000 +normal! 019| +wincmd w +argglobal +if bufexists(fnamemodify("windowtree.h", ":p")) | buffer windowtree.h | else | edit windowtree.h | endif +if &buftype ==# 'terminal' + silent file windowtree.h +endif +balt Makefile +setlocal fdm=manual +setlocal fde=0 +setlocal fmr={{{,}}} +setlocal fdi=# +setlocal fdl=0 +setlocal fml=1 +setlocal fdn=20 +setlocal fen +silent! normal! zE +let &fdl = &fdl +let s:l = 14 - ((13 * winheight(0) + 20) / 41) +if s:l < 1 | let s:l = 1 | endif +keepjumps exe s:l +normal! zt +keepjumps 14 +normal! 0 +wincmd w +argglobal +if bufexists(fnamemodify("windowtree.c", ":p")) | buffer windowtree.c | else | edit windowtree.c | endif +if &buftype ==# 'terminal' + silent file windowtree.c +endif +balt windowtree.h +setlocal fdm=manual +setlocal fde=0 +setlocal fmr={{{,}}} +setlocal fdi=# +setlocal fdl=0 +setlocal fml=1 +setlocal fdn=20 +setlocal fen +silent! normal! zE +let &fdl = &fdl +let s:l = 74 - ((38 * winheight(0) + 20) / 41) +if s:l < 1 | let s:l = 1 | endif +keepjumps exe s:l +normal! zt +keepjumps 74 +normal! 0 +wincmd w +argglobal +enew +file man://xcb_get_property_value +balt ~/projects/experiments/composegrab.c +setlocal fdm=manual +setlocal fde=0 +setlocal fmr={{{,}}} +setlocal fdi=# +setlocal fdl=0 +setlocal fml=1 +setlocal fdn=20 +setlocal nofen +wincmd w +argglobal +if bufexists(fnamemodify("~/projects/livewp/src/rootwindow.c", ":p")) | buffer ~/projects/livewp/src/rootwindow.c | else | edit ~/projects/livewp/src/rootwindow.c | endif +if &buftype ==# 'terminal' + silent file ~/projects/livewp/src/rootwindow.c +endif +balt ~/projects/experiments/composegrab.c +setlocal fdm=manual +setlocal fde=0 +setlocal fmr={{{,}}} +setlocal fdi=# +setlocal fdl=0 +setlocal fml=1 +setlocal fdn=20 +setlocal fen +silent! normal! zE +let &fdl = &fdl +let s:l = 82 - ((25 * winheight(0) + 20) / 41) +if s:l < 1 | let s:l = 1 | endif +keepjumps exe s:l +normal! zt +keepjumps 82 +normal! 0 +wincmd w +exe '1resize ' . ((&lines * 42 + 43) / 86) +exe 'vert 1resize ' . ((&columns * 106 + 159) / 319) +exe '2resize ' . ((&lines * 42 + 43) / 86) +exe 'vert 2resize ' . ((&columns * 106 + 159) / 319) +exe '3resize ' . ((&lines * 41 + 43) / 86) +exe 'vert 3resize ' . ((&columns * 106 + 159) / 319) +exe '4resize ' . ((&lines * 41 + 43) / 86) +exe 'vert 4resize ' . ((&columns * 106 + 159) / 319) +exe '5resize ' . ((&lines * 42 + 43) / 86) +exe 'vert 5resize ' . ((&columns * 105 + 159) / 319) +exe '6resize ' . ((&lines * 41 + 43) / 86) +exe 'vert 6resize ' . ((&columns * 105 + 159) / 319) +tabnext 1 +if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0 && getbufvar(s:wipebuf, '&buftype') isnot# 'terminal' + silent exe 'bwipe ' . s:wipebuf +endif +unlet! s:wipebuf +set winheight=1 winwidth=20 +let &shortmess = s:shortmess_save +let &winminheight = s:save_winminheight +let &winminwidth = s:save_winminwidth +let s:sx = expand(":p:r")."x.vim" +if filereadable(s:sx) + exe "source " . fnameescape(s:sx) +endif +let &g:so = s:so_save | let &g:siso = s:siso_save +set hlsearch +nohlsearch +doautoall SessionLoadPost +unlet SessionLoad +" vim: set ft=vim : diff --git a/img/scrall/windowtree.c b/img/scrall/windowtree.c new file mode 100644 index 0000000..2c7ba6f --- /dev/null +++ b/img/scrall/windowtree.c @@ -0,0 +1,130 @@ +// x-run: make run +#include "windowtree.h" +#include +#include +#include +#include +#include + + +struct _cookies_container { + xcb_get_property_cookie_t gp_cookie; + xcb_get_geometry_cookie_t gg_cookie; + xcb_get_window_attributes_cookie_t ga_cookie; +}; + +struct window_info *get_windows_list(xcb_connection_t *conn, size_t *n_windows) { + xcb_generic_error_t *err; + + xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; + +#if 0 + xcb_query_tree_reply_t *qt_reply; + xcb_query_tree_cookie_t qt_cookie = xcb_query_tree(conn, screen->root); + + if (!(qt_reply = xcb_query_tree_reply(conn, qt_cookie, &err))) { + fprintf(stderr, "xcb: error: xcb_query_tree_reply: %d\n", err->error_code); + return NULL; + } + + xcb_window_t *children = xcb_query_tree_children(qt_reply); + + *n_windows = xcb_query_tree_children_length(qt_reply); + struct window_info *windows = calloc(*n_windows, sizeof(struct window_info)); +#else + xcb_intern_atom_cookie_t ia_cookie = xcb_intern_atom(conn, 1, 16, "_NET_CLIENT_LIST"); + xcb_intern_atom_reply_t *ia_reply = xcb_intern_atom_reply(conn, ia_cookie, &err); + + enum xcb_atom_enum_t at_list = XCB_ATOM_WINDOW; + + /*if (!ia_reply) {*/ + /* printf("using _WIN_CLIENT_LIST\n");*/ + /* ia_cookie = xcb_intern_atom(conn, 1, 16, "_WIN_CLIENT_LIST");*/ + /* ia_reply = xcb_intern_atom_reply(conn, ia_cookie, &err);*/ + /* at_list = XCB_ATOM_CARDINAL;*/ + /*}*/ + + if (!ia_reply) { + fprintf(stderr, "xcb: error: xcb_intern_atom_reply: %d\n", err->error_code); + return NULL; + } + + xcb_get_property_cookie_t gp_client_list_cookie = xcb_get_property(conn, 0, screen->root, ia_reply->atom, at_list, 0, 1024); + free(ia_reply); + + xcb_get_property_reply_t *gp_client_list_reply = xcb_get_property_reply(conn, gp_client_list_cookie, &err); + + if (!gp_client_list_reply) { + fprintf(stderr, "xcb: error: xcb_get_property_reply: %d\n", err->error_code); + return NULL; + } + + *n_windows = xcb_get_property_value_length(gp_client_list_reply); + printf("length: %zu\n", *n_windows); + + struct window_info *windows = calloc(*n_windows, sizeof(struct window_info)); + xcb_window_t *child = (xcb_window_t *)xcb_get_property_value(gp_client_list_reply); + + for (int i = 0; i < *n_windows; i++) { + printf("WID 0x%08d\n", child[i]); + windows[i].wid = child[i]; + } + + free(gp_client_list_reply); + +#endif + + struct _cookies_container *cookies = calloc(*n_windows, sizeof(struct _cookies_container)); + // queueing requests + for (int i = 0; i < *n_windows; i++) { + cookies[i].gp_cookie = xcb_get_property(conn, 0, windows[i].wid, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1023); + cookies[i].gg_cookie = xcb_get_geometry(conn, windows[i].wid); + cookies[i].ga_cookie = xcb_get_window_attributes(conn, windows[i].wid); + } + + // reading results + for (int i = 0; i < *n_windows; i++) { + xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(conn, cookies[i].gg_cookie, &err); + if (!gg_reply) { + fprintf(stderr, "xcb: error: xcb_get_geometry: %d\n", err->error_code); + goto failure; + } + + windows[i].rect.x = gg_reply->x; + windows[i].rect.y = gg_reply->y; + windows[i].rect.width = gg_reply->width; + windows[i].rect.height = gg_reply->height; + + free(gg_reply); + + xcb_get_property_reply_t *gp_reply = xcb_get_property_reply(conn, cookies[i].gp_cookie, &err); + if (!gp_reply) { + fprintf(stderr, "xcb: error: xcb_get_property: %d\n", err->error_code); + goto failure; + } + + char *gp_value = (char *)xcb_get_property_value(gp_reply); + + memset(windows[i].title, 0, 1024); + strncpy(windows[i].title, gp_value, 1023); + + free(gp_reply); + + xcb_get_window_attributes_reply_t *ga_reply = xcb_get_window_attributes_reply(conn, cookies[i].ga_cookie, &err); + if (!gp_reply) { + fprintf(stderr, "xcb: error: xcb_get_window_attributes: %d\n", err->error_code); + goto failure; + } + + windows[i].win_gravity = ga_reply->win_gravity; + + free(ga_reply); + } + + free(cookies); // yay! + return windows; // ewww + +failure: + free(cookies); + return NULL; +} diff --git a/img/scrall/windowtree.h b/img/scrall/windowtree.h new file mode 100644 index 0000000..b422763 --- /dev/null +++ b/img/scrall/windowtree.h @@ -0,0 +1,19 @@ +#ifndef _WINDOWTREE_H_ +#define _WINDOWTREE_H_ + +#include +#include +#include +#include +#include + +struct window_info { + xcb_window_t wid; + xcb_rectangle_t rect; + char title[1024]; + uint8_t win_gravity; +}; + +struct window_info *get_windows_list(xcb_connection_t *conn, size_t *n_windows); + +#endif