npv:_reasonable_ "pedanticage" of the code
[nyanmp.git] / npv / xcb / local / code.frag.c
blob1695161a2eaf8aab65309cc3707fe4aca62e75b1
1 STATIC void npv_xcb_fatal(u8 *fmt, ...)
3 va_list ap;
5 npv_perr("xcb:");
6 va_start(ap, fmt);
7 npv_vfatal(fmt, ap);
8 va_end(ap); /* unreachable */
10 STATIC void npv_xcb_warning(u8 *fmt, ...)
12 va_list ap;
14 npv_perr("xcb:");
15 va_start(ap, fmt);
16 npv_vwarning(fmt, ap);
17 va_end(ap);
19 STATIC void npv_xcb_pout(u8 *fmt, ...)
21 va_list ap;
23 npv_pout("xcb:");
24 va_start(ap, fmt);
25 npv_vpout(fmt, ap);
26 va_end(ap);
28 STATIC void npv_xcb_libs_load(void)
30 npv_xcb_lib_l = dlopen("libxcb.so.1", RTLD_LAZY);
31 if (npv_xcb_lib_l == 0)
32 npv_xcb_fatal("%s:unable to load the xcb dynamic shared library\n", dlerror());
33 npv_xcb_xfixes_lib_l = dlopen("libxcb-xfixes.so.0", RTLD_LAZY);
34 if (npv_xcb_xfixes_lib_l == 0)
35 npv_xcb_fatal("%s:unable to load the xcb xfixes dynamic shared library\n", dlerror());
37 #define XCB_DLSYM(x) \
38 npv_dl_##x = dlsym(npv_xcb_lib_l, #x); \
39 if (npv_dl_##x == 0) \
40 npv_xcb_fatal("%s:unable to find " #x "\n", dlerror());
41 #define XCB_XFIXES_DLSYM(x) \
42 npv_dl_##x = dlsym(npv_xcb_xfixes_lib_l, #x); \
43 if (npv_dl_##x == 0) \
44 npv_xcb_fatal("%s:unable to find " #x "\n", dlerror());
45 STATIC void npv_xcb_syms(void)
47 XCB_DLSYM(xcb_connect);
48 XCB_DLSYM(xcb_get_file_descriptor);
49 XCB_DLSYM(xcb_generate_id);
50 XCB_DLSYM(xcb_connection_has_error);
51 XCB_DLSYM(xcb_get_setup);
52 XCB_DLSYM(xcb_setup_roots_length);
53 XCB_DLSYM(xcb_setup_roots_iterator);
54 XCB_DLSYM(xcb_screen_next);
55 XCB_DLSYM(xcb_create_window);
56 XCB_DLSYM(xcb_map_window);
57 XCB_DLSYM(xcb_map_window_checked);
58 XCB_DLSYM(xcb_request_check);
59 XCB_DLSYM(xcb_flush);
60 XCB_DLSYM(xcb_wait_for_event);
61 XCB_DLSYM(xcb_poll_for_event);
62 XCB_DLSYM(xcb_change_property);
63 XCB_DLSYM(xcb_disconnect);
64 XCB_DLSYM(xcb_force_screen_saver);
65 XCB_DLSYM(xcb_get_extension_data);
66 XCB_DLSYM(xcb_query_pointer);
67 XCB_DLSYM(xcb_query_pointer_reply);
68 XCB_DLSYM(xcb_send_event);
69 XCB_DLSYM(xcb_intern_atom);
70 XCB_DLSYM(xcb_intern_atom_reply);
71 XCB_XFIXES_DLSYM(xcb_xfixes_id);
72 XCB_XFIXES_DLSYM(xcb_xfixes_query_version);
73 XCB_XFIXES_DLSYM(xcb_xfixes_show_cursor);
74 XCB_XFIXES_DLSYM(xcb_xfixes_hide_cursor);
76 #undef XCB_DLSYM
77 STATIC void npv_xcb_win_create(void)
79 u32 value_mask;
80 u32 value_list[2];
82 npv_xcb_p.win_id = npv_dl_xcb_generate_id(npv_xcb_p.c);
83 npv_xcb_pout("'%s':connection:%p:screen:%d:root window id:%#x:window id=%#x\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.win_id);
85 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
86 value_list[0] = npv_xcb_p.scr->black_pixel;
87 value_list[1] = XCB_EVENT_MASK_KEY_RELEASE
88 | XCB_EVENT_MASK_STRUCTURE_NOTIFY
89 | XCB_EVENT_MASK_POINTER_MOTION
90 | XCB_EVENT_MASK_BUTTON_MOTION
91 | XCB_EVENT_MASK_LEAVE_WINDOW
92 | XCB_EVENT_MASK_ENTER_WINDOW;
93 npv_dl_xcb_create_window(npv_xcb_p.c, XCB_COPY_FROM_PARENT,
94 npv_xcb_p.win_id,
95 npv_xcb_p.scr->root, 0, 0,
96 npv_xcb_p.width, npv_xcb_p.height,
98 XCB_WINDOW_CLASS_INPUT_OUTPUT,
99 npv_xcb_p.scr->root_visual,
100 value_mask, value_list);
102 STATIC void npv_xcb_win_map(void)
104 xcb_void_cookie_t cookie;
105 xcb_generic_error_t *e;
107 cookie = npv_dl_xcb_map_window_checked(npv_xcb_p.c, npv_xcb_p.win_id);
108 npv_xcb_pout("'%s':connection:%p:screen:%d:root window id:%#x:window id:%#x:map window request cookie=%#x\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.win_id, cookie);
110 e = npv_dl_xcb_request_check(npv_xcb_p.c, cookie);
111 if (e != 0)
112 npv_xcb_fatal("'%s':connection:%p:screen:%d:root window id:%#x:window id:%#x:unable to map window\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.win_id);
113 npv_xcb_pout("'%s':connection:%p:screen:%d:root window id:%#x:window id:%#x:window mapped\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.win_id);
116 * a disp is n scrs and 1 [set of] keyboard[s] and 1 [set of] mouse[s]
117 * 1 scr could be n monitors
118 * nowdays: usually 1 scr per display
119 * 1 scr has 1 root win
121 STATIC void npv_xcb_connect(void)
123 int r;
125 npv_xcb_p.disp_env = getenv("DISPLAY");
126 if (npv_xcb_p.disp_env == 0 || npv_xcb_p.disp_env[0] == 0)
127 npv_xcb_fatal("no x11 DISPLAY environment variable, exiting\n");
129 npv_xcb_p.scr_idx = 0;
130 /* should be 0 though */
131 npv_xcb_p.c = npv_dl_xcb_connect(0, &npv_xcb_p.scr_idx);
132 r = npv_dl_xcb_connection_has_error(npv_xcb_p.c);
133 if (r > 0)
134 npv_xcb_fatal("%d:%s:error while connecting to the x11 server\n", r, npv_xcb_p.disp_env);
135 npv_xcb_pout("'%s':connection=%p, default screen index is %d (should be 0)\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx);
137 npv_xcb_p.fd = npv_dl_xcb_get_file_descriptor(npv_xcb_p.c);
138 if (npv_xcb_p.fd == -1)
139 npv_xcb_fatal("'%s':unable to get the connection file descriptor for epoll\n", npv_xcb_p.disp_env);
140 npv_xcb_pout("'%s':connection:%p:file descriptor %d\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.fd);
142 STATIC void npv_xcb_scr_get(void)
144 xcb_screen_iterator_t iter;
145 int scrs_n;
146 int i;
148 npv_xcb_p.setup = npv_dl_xcb_get_setup(npv_xcb_p.c);
150 scrs_n = npv_dl_xcb_setup_roots_length(npv_xcb_p.setup);
151 npv_xcb_pout("'%s':connection:%p:has %d screens (should be 1)\n", npv_xcb_p.disp_env, npv_xcb_p.c, scrs_n);
153 iter = npv_dl_xcb_setup_roots_iterator(npv_xcb_p.setup);
154 i = 0;
155 npv_xcb_p.scr = 0;
156 loop {
157 if (iter.rem == 0)
158 break; /* no more scr to iterate on */
160 if (i == npv_xcb_p.scr_idx) {
161 npv_xcb_p.scr = iter.data;
162 break;
164 npv_dl_xcb_screen_next(&iter);
166 npv_xcb_pout("'%s':connection:%p:screen:%d:root window id:%#x:width=%d pixels\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.scr->width_in_pixels);
167 npv_xcb_pout("'%s':connection:%p:screen:%d:root window id:%#x:height=%d pixels\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.scr->height_in_pixels);
168 npv_xcb_pout("'%s':connection:%p:screen:%d:root window id:%#x:white pixel=0x%08x\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.scr->white_pixel);
169 npv_xcb_pout("'%s':connection:%p:screen:%d:root window id:%#x:black pixel=0x%08x\n", npv_xcb_p.disp_env, npv_xcb_p.c, npv_xcb_p.scr_idx, npv_xcb_p.scr->root, npv_xcb_p.scr->black_pixel);
171 #define MIN_SZ_BIT (1 << 4)
172 #define MAX_SZ_BIT (1 << 5)
173 #define BASE_SZ_BIT (1 << 8)
174 #define FLAGS 0
175 /* 4 padding dwords */
176 #define MIN_WIDTH 5
177 #define MIN_HEIGHT 6
178 #define MAX_WIDTH 7
179 #define MAX_HEIGHT 8
180 #define BASE_WIDTH 15
181 #define BASE_HEIGHT 16
182 #define DWORDS_N 18
183 STATIC void npv_xcb_wm_hints(void)
185 #if 0
186 /* the following will tell the wm the win is not resizable */
187 u32 data[DWORDS_N];
189 memset(data, 0, sizeof(data));
190 data[FLAGS] = MIN_SZ_BIT | MAX_SZ_BIT;
192 * XXX: min == max --> the wm must understand this information as the
193 * win being non-resizeable.
194 * next: unlock the size and recreate the vulkan swpchn at each x11
195 * resize evt.
197 data[MIN_WIDTH] = npv_xcb_p.width;
198 data[MIN_HEIGHT] = npv_xcb_p.height;
199 data[MAX_WIDTH] = npv_xcb_p.width;
200 data[MAX_HEIGHT] = npv_xcb_p.height;
201 npv_dl_xcb_change_property(npv_xcb_p.c, XCB_PROP_MODE_REPLACE,
202 npv_xcb_p.win_id, XCB_ATOM_WM_NORMAL_HINTS,
203 XCB_ATOM_WM_SIZE_HINTS, 32, DWORDS_N, data);
204 #endif
205 #if 0
206 /* the following will set the base size */
207 u32 data[DWORDS_N];
209 memset(data, 0, sizeof(data));
210 data[FLAGS] = BASE_SZ_BIT;
211 data[BASE_WIDTH] = npv_xcb_p.width;
212 data[BASE_HEIGHT] = npv_xcb_p.height;
214 npv_dl_xcb_change_property(npv_xcb_p.c, XCB_PROP_MODE_REPLACE,
215 npv_xcb_p.win_id, XCB_ATOM_WM_NORMAL_HINTS,
216 XCB_ATOM_WM_SIZE_HINTS, 32, DWORDS_N, data);
217 #endif
219 #undef MIN_SZ_BIT
220 #undef MAX_SZ_BIT
221 #undef BASE_SZ_BIT
222 #undef FLAGS
223 #undef MIN_WIDTH
224 #undef MIN_HEIGHT
225 #undef MAX_WIDTH
226 #undef MAX_HEIGHT
227 #undef BASE_WIDTH
228 #undef BASE_HEIGHT
229 #undef DWORDS_N
230 #define NPV_WM_CLASS "npv\0npv"
231 STATIC void npv_xcb_wm_class(void)
233 npv_dl_xcb_change_property(npv_xcb_p.c, XCB_PROP_MODE_REPLACE,
234 npv_xcb_p.win_id, XCB_ATOM_WM_CLASS,
235 XCB_ATOM_STRING, 8, sizeof(NPV_WM_CLASS),
236 NPV_WM_CLASS);
238 #undef NPV_WM_CLASS
239 #define NPV_WM_NAME "npv"
240 STATIC void npv_xcb_wm_name(void)
242 npv_dl_xcb_change_property(npv_xcb_p.c, XCB_PROP_MODE_REPLACE,
243 npv_xcb_p.win_id, XCB_ATOM_WM_NAME,
244 XCB_ATOM_STRING, 8, strlen(NPV_WM_NAME),
245 NPV_WM_NAME);
247 #undef NPV_WM_NAME
248 STATIC void npv_xcb_evt_key_release(xcb_generic_event_t *evt)
250 u8 b;
251 xcb_key_release_event_t *key;
253 key = (xcb_key_release_event_t*)evt;
254 b = 0;
255 loop {
256 if (b == ARRAY_N(npv_x11_binds))
257 break;
258 if (key->detail == npv_x11_binds[b].keycode) {
259 npv_xcb_pout("'%s':connection:%p:event:key release:keycode:%#02x:running command for bind \"%s\"\n", npv_xcb_p.disp_env, npv_xcb_p.c, key->detail, npv_x11_binds[b].name);
260 npv_x11_binds[b].cmd();
261 return;
263 ++b;
265 npv_xcb_pout("'%s':connection:%p:event:key release:keycode:%#02x\n", npv_xcb_p.disp_env, npv_xcb_p.c, key->detail);
267 STATIC void npv_xcb_evt_leave_win(xcb_generic_event_t *evt)
269 if (npv_xcb_p.mouse_hidden) {
270 npv_dl_xcb_xfixes_show_cursor(npv_xcb_p.c, npv_xcb_p.win_id);
271 npv_dl_xcb_flush(npv_xcb_p.c);
273 npv_xcb_p.mouse_hidden = false;
274 npv_xcb_p.last_root_x = -1;
275 npv_xcb_p.last_root_y = -1;
277 STATIC void npv_xcb_mouse_visibility_timer_start(void)
279 struct itimerspec t;
280 int r;
282 memset(&t, 0, sizeof(t));
283 t.it_value.tv_sec = npv_xcb_mouse_visibility_interval_s;
284 r = timerfd_settime(npv_xcb_p.mouse_visibility_timer_fd, 0, &t, 0);
285 if (r == -1)
286 npv_xcb_fatal("unable to arm the mouse visibility timer to %u seconds\n", npv_xcb_mouse_visibility_interval_s);
288 STATIC void npv_xcb_evt_enter_win(xcb_generic_event_t *evt)
290 xcb_enter_notify_event_t *ene;
292 ene = (xcb_enter_notify_event_t*)evt;
293 npv_dl_xcb_xfixes_show_cursor(npv_xcb_p.c, npv_xcb_p.win_id);
294 npv_dl_xcb_flush(npv_xcb_p.c);
295 npv_xcb_p.mouse_hidden = false;
296 npv_xcb_p.last_root_x = ene->root_x;
297 npv_xcb_p.last_root_y = ene->root_y;
298 npv_xcb_mouse_visibility_timer_start();
300 STATIC void npv_xcb_evt_motion(xcb_generic_event_t *evt)
302 xcb_motion_notify_event_t *mne;
304 mne = (xcb_motion_notify_event_t*)evt;
305 if (npv_xcb_p.mouse_hidden) {
306 npv_dl_xcb_xfixes_show_cursor(npv_xcb_p.c, npv_xcb_p.win_id);
307 npv_dl_xcb_flush(npv_xcb_p.c);
309 npv_xcb_p.mouse_hidden = false;
310 npv_xcb_p.last_root_x = mne->root_x;
311 npv_xcb_p.last_root_y = mne->root_y;
312 npv_xcb_mouse_visibility_timer_start();
314 #if 0
315 typedef struct xcb_configure_notify_event_t {
316 uint8_t response_type;
317 uint8_t pad0;
318 uint16_t sequence;
319 xcb_window_t event;
320 xcb_window_t window;
321 xcb_window_t above_sibling;
322 int16_t x;
323 int16_t y;
324 uint16_t width;
325 uint16_t height;
326 uint16_t border_width;
327 uint8_t override_redirect;
328 uint8_t pad1;
329 } xcb_configure_notify_event_t;
330 #endif
331 STATIC void npv_xcb_evt_cfg_notify(xcb_generic_event_t *evt)
333 xcb_configure_notify_event_t *cne;
335 cne = (xcb_configure_notify_event_t*)evt;
336 npv_xcb_p.width = cne->width;
337 npv_xcb_p.height = cne->height;
338 #if 0
339 PERR("CFG_NOTIFY_EVT:x=%d y=%d w=%u h=%u bw=%u\n", cne->x, cne->y, cne->width, cne->height, cne->border_width);
340 #endif
342 STATIC void npv_xcb_evt_handle(xcb_generic_event_t *evt)
344 u8 evt_code;
346 * do not discriminate evts generated by clients using sendevent
347 * requests (note: "client message" evts have always their most
348 * significant bit set)
350 evt_code = evt->response_type & 0x7f;
352 switch (evt_code) {
353 case XCB_KEY_RELEASE:
354 npv_xcb_evt_key_release(evt);
355 break;
356 case XCB_CONFIGURE_NOTIFY:
357 npv_xcb_evt_cfg_notify(evt);
358 break;
359 case XCB_MOTION_NOTIFY:
360 npv_xcb_evt_motion(evt);
361 break;
362 case XCB_LEAVE_NOTIFY:
363 npv_xcb_evt_leave_win(evt);
364 break;
365 case XCB_ENTER_NOTIFY:
366 npv_xcb_evt_enter_win(evt);
367 break;
368 default:
369 break;
372 STATIC void npv_xcb_screensaver_heartbeat_timer_init_once(void)
374 errno = 0;
375 npv_xcb_p.screensaver_heartbeat_timer_fd = timerfd_create(
376 CLOCK_MONOTONIC, TFD_NONBLOCK);
377 if (npv_xcb_p.screensaver_heartbeat_timer_fd == -1)
378 npv_xcb_fatal("unable to get a timer file descriptor for the screensaver heartbeat:%s\n", strerror(errno));
380 STATIC void npv_xcb_mouse_visibilty_init_once(void)
382 struct xcb_query_extension_reply_t const *xfixes;
383 xcb_xfixes_query_version_cookie_t qec;
385 xfixes = npv_dl_xcb_get_extension_data(npv_xcb_p.c,
386 npv_dl_xcb_xfixes_id);
387 if (xfixes->response_type != 1 || xfixes->present == 0)
388 npv_xcb_warning("the server is missing xfixes extension\n");
389 /* the hice/show cursor is supported from version 4 */
390 npv_dl_xcb_xfixes_query_version(npv_xcb_p.c, 4, 0);
391 /*--------------------------------------------------------------------*/
392 npv_xcb_p.mouse_hidden = false;
393 npv_xcb_p.last_root_x = -1;
394 npv_xcb_p.last_root_y = -1;
396 errno = 0;
397 npv_xcb_p.mouse_visibility_timer_fd = timerfd_create(CLOCK_MONOTONIC,
398 TFD_NONBLOCK);
399 if (npv_xcb_p.mouse_visibility_timer_fd == -1)
400 npv_xcb_fatal("unable to get a timer file descriptor for the mouse visibility management:%s\n", strerror(errno));
402 #define NET_WM_STATE "_NET_WM_STATE"
403 #define NET_WM_STATE_FULLSCREEN "_NET_WM_STATE_FULLSCREEN"
404 #define SOURCE_NORMAL_APP 1
405 #define STRLEN(x) (sizeof(x)-1)
406 #define DONT_PROPAGATE_TO_ANCESTORS 0
407 STATIC void npv_xcb_fullscreen_action(u32 action)
409 xcb_intern_atom_cookie_t cookie_net_wm_state;
410 xcb_intern_atom_cookie_t cookie_net_wm_state_fullscreen;
411 xcb_intern_atom_reply_t *reply;
412 xcb_generic_error_t *err;
413 xcb_atom_t net_wm_state;
414 xcb_atom_t net_wm_state_fullscreen;
415 xcb_client_message_event_t cm_evt;
417 cookie_net_wm_state = npv_dl_xcb_intern_atom(npv_xcb_p.c, 1,
418 STRLEN(NET_WM_STATE), NET_WM_STATE);
419 cookie_net_wm_state_fullscreen = npv_dl_xcb_intern_atom(npv_xcb_p.c, 1,
420 STRLEN(NET_WM_STATE_FULLSCREEN), NET_WM_STATE_FULLSCREEN);
421 err = 0;
422 reply = npv_dl_xcb_intern_atom_reply(npv_xcb_p.c, cookie_net_wm_state,
423 &err);
424 if (reply == 0) {
425 npv_xcb_warning("unable to set fullscreen:unable to get the _NET_WM_STATE atom:%d\n", err->error_code);
426 free(err);
427 return;
429 net_wm_state = reply->atom;
430 free(reply);
431 err = 0;
432 reply = npv_dl_xcb_intern_atom_reply(npv_xcb_p.c,
433 cookie_net_wm_state_fullscreen, &err);
434 if (reply == 0) {
435 npv_xcb_warning("unable to set fullscreen:unable to get the _NET_WM_STATE_FULLSCREEN atom:%d\n", err->error_code);
436 free(err);
437 return;
439 net_wm_state_fullscreen = reply->atom;
440 free(reply);
441 /*--------------------------------------------------------------------*/
442 memset(&cm_evt, 0, sizeof(cm_evt));
443 cm_evt.response_type = XCB_CLIENT_MESSAGE;
444 cm_evt.format = 32;
445 cm_evt.window = npv_xcb_p.win_id;
446 cm_evt.type = net_wm_state;
447 cm_evt.data.data32[0] = action;
448 cm_evt.data.data32[1] = net_wm_state_fullscreen;
449 cm_evt.data.data32[3] = SOURCE_NORMAL_APP;
451 * client message send event req must use the evt mask below and must
452 * not propagate the evt, see ewmh specs
454 npv_dl_xcb_send_event(npv_xcb_p.c, DONT_PROPAGATE_TO_ANCESTORS,
455 npv_xcb_p.scr->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY
456 | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT , (u8*)&cm_evt);
457 npv_dl_xcb_flush(npv_xcb_p.c);
459 #undef NET_WM_STATE
460 #undef NET_WM_STATE_FULLSCREEN
461 #undef NET_WM_STATE_ADD
462 #undef SOURCE_NORMAL_APP
463 #undef STRLEN
464 #undef DONT_PROPAGATE_TO_ANCESTORS
465 #define NET_WM_STATE_ADD 1
466 STATIC void npv_xcb_fullscreen_init_once(bool start_fullscreen)
468 xcb_intern_atom_cookie_t cookie_net_wm_state;
469 xcb_intern_atom_cookie_t cookie_net_wm_state_fullscreen;
470 xcb_intern_atom_reply_t *reply;
471 xcb_generic_error_t *err;
472 xcb_atom_t net_wm_state;
473 xcb_atom_t net_wm_state_fullscreen;
474 xcb_client_message_event_t cm_evt;
476 if (!start_fullscreen)
477 return;
478 npv_xcb_fullscreen_action(NET_WM_STATE_ADD);
480 #undef NET_WM_STATE_ADD