Trying to list all windows from _NET_CLIENT_LIST

This commit is contained in:
Casey 2024-06-06 14:48:57 +03:00
parent 36cb48c650
commit a5cf7cabf3
Signed by: hkc
GPG Key ID: F0F6CFE11CDB0960
8 changed files with 480 additions and 68 deletions

View File

@ -1 +1,2 @@
scrall
*.o

View File

@ -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

View File

@ -1,85 +1,38 @@
// x-run: make run
#include <assert.h>
#include <raylib.h>
#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include <xcb/composite.h>
#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;
}

58
img/scrall/screenshot.c Normal file
View File

@ -0,0 +1,58 @@
#include "screenshot.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <xcb/composite.h>
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;
}

9
img/scrall/screenshot.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _SCREENSHOT_H_
#define _SCREENSHOT_H_
#include <xcb/xcb.h>
#include <raylib.h>
Image get_screenshot(xcb_connection_t *conn, xcb_window_t wid);
#endif

238
img/scrall/state Normal file
View File

@ -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("<sfile>: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("<sfile>: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 :

130
img/scrall/windowtree.c Normal file
View File

@ -0,0 +1,130 @@
// x-run: make run
#include "windowtree.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
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;
}

19
img/scrall/windowtree.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _WINDOWTREE_H_
#define _WINDOWTREE_H_
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
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