1 STATIC
void npv_xcb_libs_load(void)
3 npv_xcb_lib_l
= dlopen("libxcb.so.1", RTLD_LAZY
);
4 if (npv_xcb_lib_l
== 0)
5 FATALX("%s:unable to load the xcb dynamic shared library\n", dlerror());
6 npv_xcb_xfixes_lib_l
= dlopen("libxcb-xfixes.so.0", RTLD_LAZY
);
7 if (npv_xcb_xfixes_lib_l
== 0)
8 FATALX("%s:unable to load the xcb xfixes dynamic shared library\n", dlerror());
10 #define XCB_DLSYM(x) \
11 npv_dl_##x = dlsym(npv_xcb_lib_l, #x); \
12 if (npv_dl_##x == 0) \
13 FATALX("%s:unable to find " #x "\n", dlerror());
14 #define XCB_XFIXES_DLSYM(x) \
15 npv_dl_##x = dlsym(npv_xcb_xfixes_lib_l, #x); \
16 if (npv_dl_##x == 0) \
17 FATALX("%s:unable to find " #x "\n", dlerror());
18 STATIC
void npv_xcb_syms(void)
20 XCB_DLSYM(xcb_connect
);
21 XCB_DLSYM(xcb_get_file_descriptor
);
22 XCB_DLSYM(xcb_generate_id
);
23 XCB_DLSYM(xcb_connection_has_error
);
24 XCB_DLSYM(xcb_get_setup
);
25 XCB_DLSYM(xcb_setup_roots_length
);
26 XCB_DLSYM(xcb_setup_roots_iterator
);
27 XCB_DLSYM(xcb_screen_next
);
28 XCB_DLSYM(xcb_create_window
);
29 XCB_DLSYM(xcb_map_window
);
30 XCB_DLSYM(xcb_map_window_checked
);
31 XCB_DLSYM(xcb_request_check
);
33 XCB_DLSYM(xcb_wait_for_event
);
34 XCB_DLSYM(xcb_poll_for_event
);
35 XCB_DLSYM(xcb_change_property
);
36 XCB_DLSYM(xcb_disconnect
);
37 XCB_DLSYM(xcb_force_screen_saver
);
38 XCB_DLSYM(xcb_get_extension_data
);
39 XCB_DLSYM(xcb_query_pointer
);
40 XCB_DLSYM(xcb_query_pointer_reply
);
41 XCB_DLSYM(xcb_send_event
);
42 XCB_DLSYM(xcb_intern_atom
);
43 XCB_DLSYM(xcb_intern_atom_reply
);
44 XCB_XFIXES_DLSYM(xcb_xfixes_id
);
45 XCB_XFIXES_DLSYM(xcb_xfixes_query_version
);
46 XCB_XFIXES_DLSYM(xcb_xfixes_show_cursor
);
47 XCB_XFIXES_DLSYM(xcb_xfixes_hide_cursor
);
50 STATIC
void npv_xcb_win_create(void)
55 npv_xcb_p
.win_id
= npv_dl_xcb_generate_id(npv_xcb_p
.c
);
56 POUTX("'%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
);
58 value_mask
= XCB_CW_BACK_PIXEL
| XCB_CW_EVENT_MASK
;
59 value_list
[0] = npv_xcb_p
.scr
->black_pixel
;
60 value_list
[1] = XCB_EVENT_MASK_KEY_RELEASE
61 | XCB_EVENT_MASK_STRUCTURE_NOTIFY
62 | XCB_EVENT_MASK_POINTER_MOTION
63 | XCB_EVENT_MASK_BUTTON_MOTION
64 | XCB_EVENT_MASK_LEAVE_WINDOW
65 | XCB_EVENT_MASK_ENTER_WINDOW
;
66 npv_dl_xcb_create_window(npv_xcb_p
.c
, XCB_COPY_FROM_PARENT
,
68 npv_xcb_p
.scr
->root
, 0, 0,
69 npv_xcb_p
.width
, npv_xcb_p
.height
,
71 XCB_WINDOW_CLASS_INPUT_OUTPUT
,
72 npv_xcb_p
.scr
->root_visual
,
73 value_mask
, value_list
);
75 STATIC
void npv_xcb_win_map(void)
77 xcb_void_cookie_t cookie
;
78 xcb_generic_error_t
*e
;
80 cookie
= npv_dl_xcb_map_window_checked(npv_xcb_p
.c
, npv_xcb_p
.win_id
);
81 POUTX("'%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
);
83 e
= npv_dl_xcb_request_check(npv_xcb_p
.c
, cookie
);
85 FATALX("'%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
);
86 POUTX("'%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
);
89 * a disp is n scrs and 1 [set of] keyboard[s] and 1 [set of] mouse[s]
90 * 1 scr could be n monitors
91 * nowdays: usually 1 scr per display
92 * 1 scr has 1 root win
94 STATIC
void npv_xcb_connect(void)
98 npv_xcb_p
.disp_env
= getenv("DISPLAY");
99 if (npv_xcb_p
.disp_env
== 0 || npv_xcb_p
.disp_env
[0] == 0)
100 FATALX("no x11 DISPLAY environment variable, exiting\n");
102 npv_xcb_p
.scr_idx
= 0;
103 /* should be 0 though */
104 npv_xcb_p
.c
= npv_dl_xcb_connect(0, &npv_xcb_p
.scr_idx
);
105 r
= npv_dl_xcb_connection_has_error(npv_xcb_p
.c
);
107 FATALX("%d:%s:error while connecting to the x11 server\n", r
, npv_xcb_p
.disp_env
);
108 POUTX("'%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
);
110 npv_xcb_p
.fd
= npv_dl_xcb_get_file_descriptor(npv_xcb_p
.c
);
111 if (npv_xcb_p
.fd
== -1)
112 FATALX("'%s':unable to get the connection file descriptor for epoll\n", npv_xcb_p
.disp_env
);
113 POUTX("'%s':connection:%p:file descriptor %d\n", npv_xcb_p
.disp_env
, npv_xcb_p
.c
, npv_xcb_p
.fd
);
115 STATIC
void npv_xcb_scr_get(void)
117 xcb_screen_iterator_t iter
;
121 npv_xcb_p
.setup
= npv_dl_xcb_get_setup(npv_xcb_p
.c
);
123 scrs_n
= npv_dl_xcb_setup_roots_length(npv_xcb_p
.setup
);
124 POUTX("'%s':connection:%p:has %d screens (should be 1)\n", npv_xcb_p
.disp_env
, npv_xcb_p
.c
, scrs_n
);
126 iter
= npv_dl_xcb_setup_roots_iterator(npv_xcb_p
.setup
);
131 break; /* no more scr to iterate on */
133 if (i
== npv_xcb_p
.scr_idx
) {
134 npv_xcb_p
.scr
= iter
.data
;
137 npv_dl_xcb_screen_next(&iter
);
139 POUTX("'%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
);
140 POUTX("'%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
);
141 POUTX("'%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
);
142 POUTX("'%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
);
144 #define MIN_SZ_BIT (1 << 4)
145 #define MAX_SZ_BIT (1 << 5)
146 #define BASE_SZ_BIT (1 << 8)
148 /* 4 padding dwords */
153 #define BASE_WIDTH 15
154 #define BASE_HEIGHT 16
156 STATIC
void npv_xcb_wm_hints(void)
159 /* the following will tell the wm the win is not resizable */
162 memset(data
, 0, sizeof(data
));
163 data
[FLAGS
] = MIN_SZ_BIT
| MAX_SZ_BIT
;
165 * XXX: min == max --> the wm must understand this information as the
166 * win being non-resizeable.
167 * next: unlock the size and recreate the vulkan swpchn at each x11
170 data
[MIN_WIDTH
] = npv_xcb_p
.width
;
171 data
[MIN_HEIGHT
] = npv_xcb_p
.height
;
172 data
[MAX_WIDTH
] = npv_xcb_p
.width
;
173 data
[MAX_HEIGHT
] = npv_xcb_p
.height
;
174 npv_dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
175 npv_xcb_p
.win_id
, XCB_ATOM_WM_NORMAL_HINTS
,
176 XCB_ATOM_WM_SIZE_HINTS
, 32, DWORDS_N
, data
);
179 /* the following will set the base size */
182 memset(data
, 0, sizeof(data
));
183 data
[FLAGS
] = BASE_SZ_BIT
;
184 data
[BASE_WIDTH
] = npv_xcb_p
.width
;
185 data
[BASE_HEIGHT
] = npv_xcb_p
.height
;
187 npv_dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
188 npv_xcb_p
.win_id
, XCB_ATOM_WM_NORMAL_HINTS
,
189 XCB_ATOM_WM_SIZE_HINTS
, 32, DWORDS_N
, data
);
203 #define NPV_WM_CLASS "npv\0npv"
204 STATIC
void npv_xcb_wm_class(void)
206 npv_dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
207 npv_xcb_p
.win_id
, XCB_ATOM_WM_CLASS
,
208 XCB_ATOM_STRING
, 8, sizeof(NPV_WM_CLASS
),
212 #define NPV_WM_NAME "npv"
213 STATIC
void npv_xcb_wm_name(void)
215 npv_dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
216 npv_xcb_p
.win_id
, XCB_ATOM_WM_NAME
,
217 XCB_ATOM_STRING
, 8, strlen(NPV_WM_NAME
),
221 STATIC
void npv_xcb_evt_key_release(xcb_generic_event_t
*evt
)
224 xcb_key_release_event_t
*key
;
226 key
= (xcb_key_release_event_t
*)evt
;
229 if (b
== ARRAY_N(npv_x11_binds
))
231 if (key
->detail
== npv_x11_binds
[b
].keycode
) {
232 POUTX("'%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
);
233 npv_x11_binds
[b
].cmd();
238 POUTX("'%s':connection:%p:event:key release:keycode:%#02x\n", npv_xcb_p
.disp_env
, npv_xcb_p
.c
, key
->detail
);
240 STATIC
void npv_xcb_evt_leave_win(xcb_generic_event_t
*evt
)
242 if (npv_xcb_p
.mouse_hidden
) {
243 npv_dl_xcb_xfixes_show_cursor(npv_xcb_p
.c
, npv_xcb_p
.win_id
);
244 npv_dl_xcb_flush(npv_xcb_p
.c
);
246 npv_xcb_p
.mouse_hidden
= false;
247 npv_xcb_p
.last_root_x
= -1;
248 npv_xcb_p
.last_root_y
= -1;
250 STATIC
void npv_xcb_mouse_visibility_timer_start(void)
255 memset(&t
, 0, sizeof(t
));
256 t
.it_value
.tv_sec
= npv_xcb_mouse_visibility_interval_s
;
257 r
= timerfd_settime(npv_xcb_p
.mouse_visibility_timer_fd
, 0, &t
, 0);
259 FATALX("unable to arm the mouse visibility timer to %u seconds\n", npv_xcb_mouse_visibility_interval_s
);
261 STATIC
void npv_xcb_evt_enter_win(xcb_generic_event_t
*evt
)
263 xcb_enter_notify_event_t
*ene
;
265 ene
= (xcb_enter_notify_event_t
*)evt
;
266 npv_dl_xcb_xfixes_show_cursor(npv_xcb_p
.c
, npv_xcb_p
.win_id
);
267 npv_dl_xcb_flush(npv_xcb_p
.c
);
268 npv_xcb_p
.mouse_hidden
= false;
269 npv_xcb_p
.last_root_x
= ene
->root_x
;
270 npv_xcb_p
.last_root_y
= ene
->root_y
;
271 npv_xcb_mouse_visibility_timer_start();
273 STATIC
void npv_xcb_evt_motion(xcb_generic_event_t
*evt
)
275 xcb_motion_notify_event_t
*mne
;
277 mne
= (xcb_motion_notify_event_t
*)evt
;
278 if (npv_xcb_p
.mouse_hidden
) {
279 npv_dl_xcb_xfixes_show_cursor(npv_xcb_p
.c
, npv_xcb_p
.win_id
);
280 npv_dl_xcb_flush(npv_xcb_p
.c
);
282 npv_xcb_p
.mouse_hidden
= false;
283 npv_xcb_p
.last_root_x
= mne
->root_x
;
284 npv_xcb_p
.last_root_y
= mne
->root_y
;
285 npv_xcb_mouse_visibility_timer_start();
288 typedef struct xcb_configure_notify_event_t
{
289 uint8_t response_type
;
294 xcb_window_t above_sibling
;
299 uint16_t border_width
;
300 uint8_t override_redirect
;
302 } xcb_configure_notify_event_t
;
304 STATIC
void npv_xcb_evt_cfg_notify(xcb_generic_event_t
*evt
)
306 xcb_configure_notify_event_t
*cne
;
308 cne
= (xcb_configure_notify_event_t
*)evt
;
309 npv_xcb_p
.width
= cne
->width
;
310 npv_xcb_p
.height
= cne
->height
;
312 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
);
315 STATIC
void npv_xcb_evt_handle(xcb_generic_event_t
*evt
)
319 * do not discriminate evts generated by clients using sendevent
320 * requests (note: "client message" evts have always their most
321 * significant bit set)
323 evt_code
= evt
->response_type
& 0x7f;
326 case XCB_KEY_RELEASE
:
327 npv_xcb_evt_key_release(evt
);
329 case XCB_CONFIGURE_NOTIFY
:
330 npv_xcb_evt_cfg_notify(evt
);
332 case XCB_MOTION_NOTIFY
:
333 npv_xcb_evt_motion(evt
);
335 case XCB_LEAVE_NOTIFY
:
336 npv_xcb_evt_leave_win(evt
);
338 case XCB_ENTER_NOTIFY
:
339 npv_xcb_evt_enter_win(evt
);
345 STATIC
void npv_xcb_screensaver_heartbeat_timer_init_once(void)
348 npv_xcb_p
.screensaver_heartbeat_timer_fd
= timerfd_create(
349 CLOCK_MONOTONIC
, TFD_NONBLOCK
);
350 if (npv_xcb_p
.screensaver_heartbeat_timer_fd
== -1)
351 FATALX("unable to get a timer file descriptor for the screensaver heartbeat:%s\n", strerror(errno
));
353 STATIC
void npv_xcb_mouse_visibilty_init_once(void)
355 struct xcb_query_extension_reply_t
const *xfixes
;
356 xcb_xfixes_query_version_cookie_t qec
;
358 xfixes
= npv_dl_xcb_get_extension_data(npv_xcb_p
.c
,
359 npv_dl_xcb_xfixes_id
);
360 if (xfixes
->response_type
!= 1 || xfixes
->present
== 0)
361 WARNINGX("the server is missing xfixes extension\n");
362 /* the hice/show cursor is supported from version 4 */
363 npv_dl_xcb_xfixes_query_version(npv_xcb_p
.c
, 4, 0);
364 /*--------------------------------------------------------------------*/
365 npv_xcb_p
.mouse_hidden
= false;
366 npv_xcb_p
.last_root_x
= -1;
367 npv_xcb_p
.last_root_y
= -1;
370 npv_xcb_p
.mouse_visibility_timer_fd
= timerfd_create(CLOCK_MONOTONIC
,
372 if (npv_xcb_p
.mouse_visibility_timer_fd
== -1)
373 FATALX("unable to get a timer file descriptor for the mouse visibility management:%s\n", strerror(errno
));
375 #define NET_WM_STATE "_NET_WM_STATE"
376 #define NET_WM_STATE_FULLSCREEN "_NET_WM_STATE_FULLSCREEN"
377 #define SOURCE_NORMAL_APP 1
378 #define STRLEN(x) (sizeof(x)-1)
379 #define DONT_PROPAGATE_TO_ANCESTORS 0
380 STATIC
void npv_xcb_fullscreen_action(u32 action
)
382 xcb_intern_atom_cookie_t cookie_net_wm_state
;
383 xcb_intern_atom_cookie_t cookie_net_wm_state_fullscreen
;
384 xcb_intern_atom_reply_t
*reply
;
385 xcb_generic_error_t
*err
;
386 xcb_atom_t net_wm_state
;
387 xcb_atom_t net_wm_state_fullscreen
;
388 xcb_client_message_event_t cm_evt
;
390 cookie_net_wm_state
= npv_dl_xcb_intern_atom(npv_xcb_p
.c
, 1,
391 STRLEN(NET_WM_STATE
), NET_WM_STATE
);
392 cookie_net_wm_state_fullscreen
= npv_dl_xcb_intern_atom(npv_xcb_p
.c
, 1,
393 STRLEN(NET_WM_STATE_FULLSCREEN
), NET_WM_STATE_FULLSCREEN
);
395 reply
= npv_dl_xcb_intern_atom_reply(npv_xcb_p
.c
, cookie_net_wm_state
,
398 WARNINGX("unable to set fullscreen:unable to get the _NET_WM_STATE atom:%d\n", err
->error_code
);
402 net_wm_state
= reply
->atom
;
405 reply
= npv_dl_xcb_intern_atom_reply(npv_xcb_p
.c
,
406 cookie_net_wm_state_fullscreen
, &err
);
408 WARNINGX("unable to set fullscreen:unable to get the _NET_WM_STATE_FULLSCREEN atom:%d\n", err
->error_code
);
412 net_wm_state_fullscreen
= reply
->atom
;
414 /*--------------------------------------------------------------------*/
415 memset(&cm_evt
, 0, sizeof(cm_evt
));
416 cm_evt
.response_type
= XCB_CLIENT_MESSAGE
;
418 cm_evt
.window
= npv_xcb_p
.win_id
;
419 cm_evt
.type
= net_wm_state
;
420 cm_evt
.data
.data32
[0] = action
;
421 cm_evt
.data
.data32
[1] = net_wm_state_fullscreen
;
422 cm_evt
.data
.data32
[3] = SOURCE_NORMAL_APP
;
424 * client message send event req must use the evt mask below and must
425 * not propagate the evt, see ewmh specs
427 npv_dl_xcb_send_event(npv_xcb_p
.c
, DONT_PROPAGATE_TO_ANCESTORS
,
428 npv_xcb_p
.scr
->root
, XCB_EVENT_MASK_STRUCTURE_NOTIFY
429 | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
, (u8
*)&cm_evt
);
430 npv_dl_xcb_flush(npv_xcb_p
.c
);
433 #undef NET_WM_STATE_FULLSCREEN
434 #undef NET_WM_STATE_ADD
435 #undef SOURCE_NORMAL_APP
437 #undef DONT_PROPAGATE_TO_ANCESTORS
438 #define NET_WM_STATE_ADD 1
439 STATIC
void npv_xcb_fullscreen_init_once(bool start_fullscreen
)
441 xcb_intern_atom_cookie_t cookie_net_wm_state
;
442 xcb_intern_atom_cookie_t cookie_net_wm_state_fullscreen
;
443 xcb_intern_atom_reply_t
*reply
;
444 xcb_generic_error_t
*err
;
445 xcb_atom_t net_wm_state
;
446 xcb_atom_t net_wm_state_fullscreen
;
447 xcb_client_message_event_t cm_evt
;
449 if (!start_fullscreen
)
451 npv_xcb_fullscreen_action(NET_WM_STATE_ADD
);
453 #undef NET_WM_STATE_ADD