npv:aspect ratio handling
[nyanmp.git] / npv / xcb / local / code.frag.c
blobedc05ed3dcb1c9f326ac1535e80f5253217a4c41
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); \
12 if (dl_##x == 0) \
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); \
16 if (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);
32 XCB_DLSYM(xcb_flush);
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);
49 #undef XCB_DLSYM
50 static void npv_xcb_win_create(void)
52 u32 value_mask;
53 u32 value_list[2];
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,
67 npv_xcb_p.win_id,
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);
84 if (e != 0)
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)
96 int r;
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);
105 if (r > 0)
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;
117 int scrs_n;
118 int i;
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);
126 i = 0;
127 npv_xcb_p.scr = 0;
128 loop {
129 if (iter.rem == 0)
130 break; /* no more scr to iterate on */
132 if (i == npv_xcb_p.scr_idx) {
133 npv_xcb_p.scr = iter.data;
134 break;
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)
146 #define FLAGS 0
147 /* 4 padding dwords */
148 #define MIN_WIDTH 5
149 #define MIN_HEIGHT 6
150 #define MAX_WIDTH 7
151 #define MAX_HEIGHT 8
152 #define BASE_WIDTH 15
153 #define BASE_HEIGHT 16
154 #define DWORDS_N 18
155 static void npv_xcb_wm_hints(void)
157 #if 0
158 /* the following will tell the wm the win is not resizable */
159 u32 data[DWORDS_N];
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
167 * resize evt.
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);
176 #endif
177 #if 0
178 /* the following will set the base size */
179 u32 data[DWORDS_N];
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);
189 #endif
191 #undef MIN_SZ_BIT
192 #undef MAX_SZ_BIT
193 #undef BASE_SZ_BIT
194 #undef FLAGS
195 #undef MIN_WIDTH
196 #undef MIN_HEIGHT
197 #undef MAX_WIDTH
198 #undef MAX_HEIGHT
199 #undef BASE_WIDTH
200 #undef BASE_HEIGHT
201 #undef DWORDS_N
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),
208 NPV_WM_CLASS);
210 #undef 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),
217 NPV_WM_NAME);
219 #undef NPV_WM_NAME
220 static void npv_xcb_evt_key_release(xcb_generic_event_t *evt)
222 u8 b;
223 xcb_key_release_event_t *key;
225 key = (xcb_key_release_event_t*)evt;
226 b = 0;
227 loop {
228 if (b == ARRAY_N(x11_binds))
229 break;
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);
232 x11_binds[b].cmd();
233 return;
235 ++b;
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)
251 struct itimerspec t;
252 int r;
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);
257 if (r == -1)
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();
286 #if 0
287 typedef struct xcb_configure_notify_event_t {
288 uint8_t response_type;
289 uint8_t pad0;
290 uint16_t sequence;
291 xcb_window_t event;
292 xcb_window_t window;
293 xcb_window_t above_sibling;
294 int16_t x;
295 int16_t y;
296 uint16_t width;
297 uint16_t height;
298 uint16_t border_width;
299 uint8_t override_redirect;
300 uint8_t pad1;
301 } xcb_configure_notify_event_t;
302 #endif
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;
310 #if 0
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);
312 #endif
314 static void npv_xcb_evt_handle(xcb_generic_event_t *evt)
316 u8 evt_code;
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;
324 switch (evt_code) {
325 case XCB_KEY_RELEASE:
326 npv_xcb_evt_key_release(evt);
327 break;
328 case XCB_CONFIGURE_NOTIFY:
329 npv_xcb_evt_cfg_notify(evt);
330 break;
331 case XCB_MOTION_NOTIFY:
332 npv_xcb_evt_motion(evt);
333 break;
334 case XCB_LEAVE_NOTIFY:
335 npv_xcb_evt_leave_win(evt);
336 break;
337 case XCB_ENTER_NOTIFY:
338 npv_xcb_evt_enter_win(evt);
339 break;
340 default:
341 break;
344 static void npv_xcb_screensaver_heartbeat_timer_init_once(void)
346 errno = 0;
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;
367 errno = 0;
368 npv_xcb_p.mouse_visibility_timer_fd = timerfd_create(CLOCK_MONOTONIC,
369 TFD_NONBLOCK);
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);
392 err = 0;
393 reply = dl_xcb_intern_atom_reply(npv_xcb_p.c, cookie_net_wm_state,
394 &err);
395 if (reply == 0) {
396 WARNINGX("unable to set fullscreen:unable to get the _NET_WM_STATE atom:%d\n", err->error_code);
397 free(err);
398 return;
400 net_wm_state = reply->atom;
401 free(reply);
402 err = 0;
403 reply = dl_xcb_intern_atom_reply(npv_xcb_p.c,
404 cookie_net_wm_state_fullscreen, &err);
405 if (reply == 0) {
406 WARNINGX("unable to set fullscreen:unable to get the _NET_WM_STATE_FULLSCREEN atom:%d\n", err->error_code);
407 free(err);
408 return;
410 net_wm_state_fullscreen = reply->atom;
411 free(reply);
412 /*--------------------------------------------------------------------*/
413 memset(&cm_evt, 0, sizeof(cm_evt));
414 cm_evt.response_type = XCB_CLIENT_MESSAGE;
415 cm_evt.format = 32;
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);
430 #undef NET_WM_STATE
431 #undef NET_WM_STATE_FULLSCREEN
432 #undef NET_WM_STATE_ADD
433 #undef SOURCE_NORMAL_APP
434 #undef STRLEN
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)
448 return;
449 npv_xcb_fullscreen_action(NET_WM_STATE_ADD);
451 #undef NET_WM_STATE_ADD