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 dl_##x = dlsym(npv_xcb_lib_l, #x); \
13 FATALX("%s:unable to find " #x "\n", dlerror());
14 #define XCB_XFIXES_DLSYM(x) \
15 dl_##x = dlsym(npv_xcb_xfixes_lib_l, #x); \
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
= 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 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
= 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
= 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 npv_xcb_p
.c
= dl_xcb_connect(0, &npv_xcb_p
.scr_idx
); /* should be 0 though */
104 r
= dl_xcb_connection_has_error(npv_xcb_p
.c
);
106 FATALX("%d:%s:error while connecting to the x11 server\n", r
, npv_xcb_p
.disp_env
);
107 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
);
109 npv_xcb_p
.fd
= dl_xcb_get_file_descriptor(npv_xcb_p
.c
);
110 if (npv_xcb_p
.fd
== -1)
111 FATALX("'%s':unable to get the connection file descriptor for epoll\n", npv_xcb_p
.disp_env
);
112 POUTX("'%s':connection:%p:file descriptor %d\n", npv_xcb_p
.disp_env
, npv_xcb_p
.c
, npv_xcb_p
.fd
);
114 static void npv_xcb_scr_get(void)
116 xcb_screen_iterator_t iter
;
120 npv_xcb_p
.setup
= dl_xcb_get_setup(npv_xcb_p
.c
);
122 scrs_n
= dl_xcb_setup_roots_length(npv_xcb_p
.setup
);
123 POUTX("'%s':connection:%p:has %d screens (should be 1)\n", npv_xcb_p
.disp_env
, npv_xcb_p
.c
, scrs_n
);
125 iter
= dl_xcb_setup_roots_iterator(npv_xcb_p
.setup
);
130 break; /* no more scr to iterate on */
132 if (i
== npv_xcb_p
.scr_idx
) {
133 npv_xcb_p
.scr
= iter
.data
;
136 dl_xcb_screen_next(&iter
);
138 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
);
139 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
);
140 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
);
141 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
);
143 #define MIN_SZ_BIT (1 << 4)
144 #define MAX_SZ_BIT (1 << 5)
145 #define BASE_SZ_BIT (1 << 8)
147 /* 4 padding dwords */
152 #define BASE_WIDTH 15
153 #define BASE_HEIGHT 16
155 static void npv_xcb_wm_hints(void)
158 /* the following will tell the wm the win is not resizable */
161 memset(data
, 0, sizeof(data
));
162 data
[FLAGS
] = MIN_SZ_BIT
| MAX_SZ_BIT
;
164 * XXX: min == max --> the wm must understand this information as the
165 * win being non-resizeable.
166 * next: unlock the size and recreate the vulkan swpchn at each x11
169 data
[MIN_WIDTH
] = npv_xcb_p
.width
;
170 data
[MIN_HEIGHT
] = npv_xcb_p
.height
;
171 data
[MAX_WIDTH
] = npv_xcb_p
.width
;
172 data
[MAX_HEIGHT
] = npv_xcb_p
.height
;
173 dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
174 npv_xcb_p
.win_id
, XCB_ATOM_WM_NORMAL_HINTS
,
175 XCB_ATOM_WM_SIZE_HINTS
, 32, DWORDS_N
, data
);
178 /* the following will set the base size */
181 memset(data
, 0, sizeof(data
));
182 data
[FLAGS
] = BASE_SZ_BIT
;
183 data
[BASE_WIDTH
] = npv_xcb_p
.width
;
184 data
[BASE_HEIGHT
] = npv_xcb_p
.height
;
186 dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
187 npv_xcb_p
.win_id
, XCB_ATOM_WM_NORMAL_HINTS
,
188 XCB_ATOM_WM_SIZE_HINTS
, 32, DWORDS_N
, data
);
202 #define NPV_WM_CLASS "npv\0npv"
203 static void npv_xcb_wm_class(void)
205 dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
206 npv_xcb_p
.win_id
, XCB_ATOM_WM_CLASS
,
207 XCB_ATOM_STRING
, 8, sizeof(NPV_WM_CLASS
),
211 #define NPV_WM_NAME "npv"
212 static void npv_xcb_wm_name(void)
214 dl_xcb_change_property(npv_xcb_p
.c
, XCB_PROP_MODE_REPLACE
,
215 npv_xcb_p
.win_id
, XCB_ATOM_WM_NAME
,
216 XCB_ATOM_STRING
, 8, strlen(NPV_WM_NAME
),
220 static void npv_xcb_evt_key_release(xcb_generic_event_t
*evt
)
223 xcb_key_release_event_t
*key
;
225 key
= (xcb_key_release_event_t
*)evt
;
228 if (b
== ARRAY_N(x11_binds
))
230 if (key
->detail
== x11_binds
[b
].keycode
) {
231 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
, x11_binds
[b
].name
);
237 POUTX("'%s':connection:%p:event:key release:keycode:%#02x\n", npv_xcb_p
.disp_env
, npv_xcb_p
.c
, key
->detail
);
239 static void npv_xcb_evt_leave_win(xcb_generic_event_t
*evt
)
241 if (npv_xcb_p
.mouse_hidden
) {
242 dl_xcb_xfixes_show_cursor(npv_xcb_p
.c
, npv_xcb_p
.win_id
);
243 dl_xcb_flush(npv_xcb_p
.c
);
245 npv_xcb_p
.mouse_hidden
= false;
246 npv_xcb_p
.last_root_x
= -1;
247 npv_xcb_p
.last_root_y
= -1;
249 static void npv_xcb_mouse_visibility_timer_start(void)
254 memset(&t
, 0, sizeof(t
));
255 t
.it_value
.tv_sec
= npv_xcb_mouse_visibility_interval_s
;
256 r
= timerfd_settime(npv_xcb_p
.mouse_visibility_timer_fd
, 0, &t
, 0);
258 FATALX("unable to arm the mouse visibility timer to %u seconds\n", npv_xcb_mouse_visibility_interval_s
);
260 static void npv_xcb_evt_enter_win(xcb_generic_event_t
*evt
)
262 xcb_enter_notify_event_t
*ene
;
264 ene
= (xcb_enter_notify_event_t
*)evt
;
265 dl_xcb_xfixes_show_cursor(npv_xcb_p
.c
, npv_xcb_p
.win_id
);
266 dl_xcb_flush(npv_xcb_p
.c
);
267 npv_xcb_p
.mouse_hidden
= false;
268 npv_xcb_p
.last_root_x
= ene
->root_x
;
269 npv_xcb_p
.last_root_y
= ene
->root_y
;
270 npv_xcb_mouse_visibility_timer_start();
272 static void npv_xcb_evt_motion(xcb_generic_event_t
*evt
)
274 xcb_motion_notify_event_t
*mne
;
276 mne
= (xcb_motion_notify_event_t
*)evt
;
277 if (npv_xcb_p
.mouse_hidden
) {
278 dl_xcb_xfixes_show_cursor(npv_xcb_p
.c
, npv_xcb_p
.win_id
);
279 dl_xcb_flush(npv_xcb_p
.c
);
281 npv_xcb_p
.mouse_hidden
= false;
282 npv_xcb_p
.last_root_x
= mne
->root_x
;
283 npv_xcb_p
.last_root_y
= mne
->root_y
;
284 npv_xcb_mouse_visibility_timer_start();
287 typedef struct xcb_configure_notify_event_t
{
288 uint8_t response_type
;
293 xcb_window_t above_sibling
;
298 uint16_t border_width
;
299 uint8_t override_redirect
;
301 } xcb_configure_notify_event_t
;
303 static void npv_xcb_evt_cfg_notify(xcb_generic_event_t
*evt
)
305 xcb_configure_notify_event_t
*cne
;
307 cne
= (xcb_configure_notify_event_t
*)evt
;
308 npv_xcb_p
.width
= cne
->width
;
309 npv_xcb_p
.height
= cne
->height
;
311 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
);
314 static void npv_xcb_evt_handle(xcb_generic_event_t
*evt
)
318 * do not discriminate evts generated by clients using sendevent
319 * requests (note: "client message" evts have always their most
320 * significant bit set)
322 evt_code
= evt
->response_type
& 0x7f;
325 case XCB_KEY_RELEASE
:
326 npv_xcb_evt_key_release(evt
);
328 case XCB_CONFIGURE_NOTIFY
:
329 npv_xcb_evt_cfg_notify(evt
);
331 case XCB_MOTION_NOTIFY
:
332 npv_xcb_evt_motion(evt
);
334 case XCB_LEAVE_NOTIFY
:
335 npv_xcb_evt_leave_win(evt
);
337 case XCB_ENTER_NOTIFY
:
338 npv_xcb_evt_enter_win(evt
);
344 static void npv_xcb_screensaver_heartbeat_timer_init_once(void)
347 npv_xcb_p
.screensaver_heartbeat_timer_fd
= timerfd_create(
348 CLOCK_MONOTONIC
, TFD_NONBLOCK
);
349 if (npv_xcb_p
.screensaver_heartbeat_timer_fd
== -1)
350 FATALX("unable to get a timer file descriptor for the screensaver heartbeat:%s\n", strerror(errno
));
352 static void npv_xcb_mouse_visibilty_init_once(void)
354 struct xcb_query_extension_reply_t
const *xfixes
;
355 xcb_xfixes_query_version_cookie_t qec
;
357 xfixes
= dl_xcb_get_extension_data(npv_xcb_p
.c
, dl_xcb_xfixes_id
);
358 if (xfixes
->response_type
!= 1 || xfixes
->present
== 0)
359 WARNINGX("the server is missing xfixes extension\n");
360 /* the hice/show cursor is supported from version 4 */
361 dl_xcb_xfixes_query_version(npv_xcb_p
.c
, 4, 0);
362 /*--------------------------------------------------------------------*/
363 npv_xcb_p
.mouse_hidden
= false;
364 npv_xcb_p
.last_root_x
= -1;
365 npv_xcb_p
.last_root_y
= -1;
368 npv_xcb_p
.mouse_visibility_timer_fd
= timerfd_create(CLOCK_MONOTONIC
,
370 if (npv_xcb_p
.mouse_visibility_timer_fd
== -1)
371 FATALX("unable to get a timer file descriptor for the mouse visibility management:%s\n", strerror(errno
));
373 #define NET_WM_STATE "_NET_WM_STATE"
374 #define NET_WM_STATE_FULLSCREEN "_NET_WM_STATE_FULLSCREEN"
375 #define SOURCE_NORMAL_APP 1
376 #define STRLEN(x) (sizeof(x)-1)
377 #define DONT_PROPAGATE_TO_ANCESTORS 0
378 static void npv_xcb_fullscreen_action(u32 action
)
380 xcb_intern_atom_cookie_t cookie_net_wm_state
;
381 xcb_intern_atom_cookie_t cookie_net_wm_state_fullscreen
;
382 xcb_intern_atom_reply_t
*reply
;
383 xcb_generic_error_t
*err
;
384 xcb_atom_t net_wm_state
;
385 xcb_atom_t net_wm_state_fullscreen
;
386 xcb_client_message_event_t cm_evt
;
388 cookie_net_wm_state
= dl_xcb_intern_atom(npv_xcb_p
.c
, 1,
389 STRLEN(NET_WM_STATE
), NET_WM_STATE
);
390 cookie_net_wm_state_fullscreen
= dl_xcb_intern_atom(npv_xcb_p
.c
, 1,
391 STRLEN(NET_WM_STATE_FULLSCREEN
), NET_WM_STATE_FULLSCREEN
);
393 reply
= dl_xcb_intern_atom_reply(npv_xcb_p
.c
, cookie_net_wm_state
,
396 WARNINGX("unable to set fullscreen:unable to get the _NET_WM_STATE atom:%d\n", err
->error_code
);
400 net_wm_state
= reply
->atom
;
403 reply
= dl_xcb_intern_atom_reply(npv_xcb_p
.c
,
404 cookie_net_wm_state_fullscreen
, &err
);
406 WARNINGX("unable to set fullscreen:unable to get the _NET_WM_STATE_FULLSCREEN atom:%d\n", err
->error_code
);
410 net_wm_state_fullscreen
= reply
->atom
;
412 /*--------------------------------------------------------------------*/
413 memset(&cm_evt
, 0, sizeof(cm_evt
));
414 cm_evt
.response_type
= XCB_CLIENT_MESSAGE
;
416 cm_evt
.window
= npv_xcb_p
.win_id
;
417 cm_evt
.type
= net_wm_state
;
418 cm_evt
.data
.data32
[0] = action
;
419 cm_evt
.data
.data32
[1] = net_wm_state_fullscreen
;
420 cm_evt
.data
.data32
[3] = SOURCE_NORMAL_APP
;
422 * client message send event req must use the evt mask below and must
423 * not propagate the evt, see ewmh specs
425 dl_xcb_send_event(npv_xcb_p
.c
, DONT_PROPAGATE_TO_ANCESTORS
,
426 npv_xcb_p
.scr
->root
, XCB_EVENT_MASK_STRUCTURE_NOTIFY
427 | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
, (u8
*)&cm_evt
);
428 dl_xcb_flush(npv_xcb_p
.c
);
431 #undef NET_WM_STATE_FULLSCREEN
432 #undef NET_WM_STATE_ADD
433 #undef SOURCE_NORMAL_APP
435 #undef DONT_PROPAGATE_TO_ANCESTORS
436 #define NET_WM_STATE_ADD 1
437 static void npv_xcb_fullscreen_init_once(bool start_fullscreen
)
439 xcb_intern_atom_cookie_t cookie_net_wm_state
;
440 xcb_intern_atom_cookie_t cookie_net_wm_state_fullscreen
;
441 xcb_intern_atom_reply_t
*reply
;
442 xcb_generic_error_t
*err
;
443 xcb_atom_t net_wm_state
;
444 xcb_atom_t net_wm_state_fullscreen
;
445 xcb_client_message_event_t cm_evt
;
447 if (!start_fullscreen
)
449 npv_xcb_fullscreen_action(NET_WM_STATE_ADD
);
451 #undef NET_WM_STATE_ADD