rofi 1.7.5
xcb.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2012 Sean Pringle <sean.pringle@gmail.com>
6 * Copyright © 2013-2022 Qball Cow <qball@gmpclient.org>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 */
28
30#define G_LOG_DOMAIN "X11Helper"
31
32#include "config.h"
33#include <cairo-xcb.h>
34#include <cairo.h>
35#include <glib.h>
36#include <math.h>
37#include <stdint.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include <xcb/randr.h>
43#include <xcb/xcb.h>
44#include <xcb/xcb_aux.h>
45#include <xcb/xcb_cursor.h>
46#include <xcb/xcb_ewmh.h>
47#include <xcb/xinerama.h>
48#include <xcb/xkb.h>
49#include <xcb/xproto.h>
50#include <xkbcommon/xkbcommon-x11.h>
51#include <xkbcommon/xkbcommon.h>
53#define SN_API_NOT_YET_FROZEN
56#define sn_launcher_context_set_application_id sn_launcher_set_application_id
57#include "display.h"
58#include "helper.h"
59#include "rofi-types.h"
60#include "settings.h"
61#include "timings.h"
62#include "xcb-internal.h"
63#include "xcb.h"
64#include <libsn/sn.h>
65
66#include "mode.h"
67#include "modes/window.h"
68
69#include <rofi.h>
70
72#define RANDR_PREF_MAJOR_VERSION 1
74#define RANDR_PREF_MINOR_VERSION 5
75
77#define INTERSECT(x, y, x1, y1, w1, h1) \
78 ((((x) >= (x1)) && ((x) < (x1 + w1))) && (((y) >= (y1)) && ((y) < (y1 + h1))))
79
81
85struct _xcb_stuff xcb_int = {.connection = NULL,
86 .screen = NULL,
87 .screen_nbr = -1,
88 .sndisplay = NULL,
89 .sncontext = NULL,
90 .monitors = NULL};
92
96xcb_depth_t *depth = NULL;
97xcb_visualtype_t *visual = NULL;
98xcb_colormap_t map = XCB_COLORMAP_NONE;
102static xcb_visualtype_t *root_visual = NULL;
105
109xcb_cursor_t cursors[NUM_CURSORS] = {XCB_CURSOR_NONE, XCB_CURSOR_NONE,
110 XCB_CURSOR_NONE};
111
113const struct {
115 const char *css_name;
117 const char *traditional_name;
118} cursor_names[] = {
119 {"default", "left_ptr"}, {"pointer", "hand"}, {"text", "xterm"}};
120
121static xcb_visualtype_t *lookup_visual(xcb_screen_t *s, xcb_visualid_t visual) {
122 xcb_depth_iterator_t d;
123 d = xcb_screen_allowed_depths_iterator(s);
124 for (; d.rem; xcb_depth_next(&d)) {
125 xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator(d.data);
126 for (; v.rem; xcb_visualtype_next(&v)) {
127 if (v.data->visual_id == visual) {
128 return v.data;
129 }
130 }
131 }
132 return 0;
133}
134
135/* This blur function was originally created my MacSlow and published on his
136 * website: http://macslow.thepimp.net. I'm not entirely sure he's proud of it,
137 * but it has proved immeasurably useful for me. */
138
139static uint32_t *create_kernel(double radius, double deviation,
140 uint32_t *sum2) {
141 int size = 2 * (int)(radius) + 1;
142 uint32_t *kernel = (uint32_t *)(g_malloc(sizeof(uint32_t) * (size + 1)));
143 double radiusf = fabs(radius) + 1.0;
144 double value = -radius;
145 double sum = 0.0;
146 int i;
147
148 if (deviation == 0.0) {
149 deviation = sqrt(-(radiusf * radiusf) / (2.0 * log(1.0 / 255.0)));
150 }
151
152 kernel[0] = size;
153
154 for (i = 0; i < size; i++) {
155 kernel[1 + i] = INT16_MAX / (2.506628275 * deviation) *
156 exp(-((value * value) / (2.0 * (deviation * deviation))));
157
158 sum += kernel[1 + i];
159 value += 1.0;
160 }
161
162 *sum2 = sum;
163
164 return kernel;
165}
166
167void cairo_image_surface_blur(cairo_surface_t *surface, double radius,
168 double deviation) {
169 uint32_t *horzBlur;
170 uint32_t *kernel = 0;
171 cairo_format_t format;
172 unsigned int channels;
173
174 if (cairo_surface_status(surface)) {
175 return;
176 }
177
178 uint8_t *data = cairo_image_surface_get_data(surface);
179 format = cairo_image_surface_get_format(surface);
180 const int width = cairo_image_surface_get_width(surface);
181 const int height = cairo_image_surface_get_height(surface);
182 const int stride = cairo_image_surface_get_stride(surface);
183
184 if (format == CAIRO_FORMAT_ARGB32) {
185 channels = 4;
186 } else {
187 return;
188 }
189
190 horzBlur = (uint32_t *)(g_malloc(sizeof(uint32_t) * height * stride));
191 TICK();
192 uint32_t sum = 0;
193 kernel = create_kernel(radius, deviation, &sum);
194 TICK_N("BLUR: kernel");
195
196 /* Horizontal pass. */
197 uint32_t *horzBlur_ptr = horzBlur;
198 for (int iY = 0; iY < height; iY++) {
199 const int iYs = iY * stride;
200 for (int iX = 0; iX < width; iX++) {
201 uint32_t red = 0;
202 uint32_t green = 0;
203 uint32_t blue = 0;
204 uint32_t alpha = 0;
205 int offset = (int)(kernel[0]) / -2;
206
207 for (int i = 0; i < (int)(kernel[0]); i++) {
208 int x = iX + offset;
209
210 if (x < 0 || x >= width) {
211 offset++;
212 continue;
213 }
214
215 uint8_t *dataPtr = &data[iYs + x * channels];
216 const uint32_t kernip1 = kernel[i + 1];
217
218 blue += kernip1 * dataPtr[0];
219 green += kernip1 * dataPtr[1];
220 red += kernip1 * dataPtr[2];
221 alpha += kernip1 * dataPtr[3];
222 offset++;
223 }
224
225 *horzBlur_ptr++ = blue / sum;
226 *horzBlur_ptr++ = green / sum;
227 *horzBlur_ptr++ = red / sum;
228 *horzBlur_ptr++ = alpha / sum;
229 }
230 }
231 TICK_N("BLUR: hori");
232
233 /* Vertical pass. */
234 for (int iY = 0; iY < height; iY++) {
235 for (int iX = 0; iX < width; iX++) {
236 uint32_t red = 0;
237 uint32_t green = 0;
238 uint32_t blue = 0;
239 uint32_t alpha = 0;
240 int offset = (int)(kernel[0]) / -2;
241
242 const int iXs = iX * channels;
243 for (int i = 0; i < (int)(kernel[0]); i++) {
244 int y = iY + offset;
245
246 if (y < 0 || y >= height) {
247 offset++;
248 continue;
249 }
250
251 uint32_t *dataPtr = &horzBlur[y * stride + iXs];
252 const uint32_t kernip1 = kernel[i + 1];
253
254 blue += kernip1 * dataPtr[0];
255 green += kernip1 * dataPtr[1];
256 red += kernip1 * dataPtr[2];
257 alpha += kernip1 * dataPtr[3];
258
259 offset++;
260 }
261
262 *data++ = blue / sum;
263 *data++ = green / sum;
264 *data++ = red / sum;
265 *data++ = alpha / sum;
266 }
267 }
268 TICK_N("BLUR: vert");
269
270 free(kernel);
271 free(horzBlur);
272
273 return;
274}
275
276cairo_surface_t *x11_helper_get_screenshot_surface_window(xcb_window_t window,
277 int size) {
278 xcb_get_geometry_cookie_t cookie;
279 xcb_get_geometry_reply_t *reply;
280
281 cookie = xcb_get_geometry(xcb->connection, window);
282 reply = xcb_get_geometry_reply(xcb->connection, cookie, NULL);
283 if (reply == NULL) {
284 return NULL;
285 }
286
287 xcb_get_window_attributes_cookie_t attributesCookie =
288 xcb_get_window_attributes(xcb->connection, window);
289 xcb_get_window_attributes_reply_t *attributes =
290 xcb_get_window_attributes_reply(xcb->connection, attributesCookie, NULL);
291 if (attributes == NULL || (attributes->map_state != XCB_MAP_STATE_VIEWABLE)) {
292 free(reply);
293 if (attributes) {
294 free(attributes);
295 }
296 return NULL;
297 }
298 // Create a cairo surface for the window.
299 xcb_visualtype_t *vt = lookup_visual(xcb->screen, attributes->visual);
300 free(attributes);
301
302 cairo_surface_t *t = cairo_xcb_surface_create(xcb->connection, window, vt,
303 reply->width, reply->height);
304
305 if (cairo_surface_status(t) != CAIRO_STATUS_SUCCESS) {
306 cairo_surface_destroy(t);
307 free(reply);
308 return NULL;
309 }
310
311 // Scale the image, as we don't want to keep large one around.
312 int max = MAX(reply->width, reply->height);
313 double scale = (double)size / max;
314
315 cairo_surface_t *s2 = cairo_surface_create_similar_image(
316 t, CAIRO_FORMAT_ARGB32, reply->width * scale, reply->height * scale);
317 free(reply);
318
319 if (cairo_surface_status(s2) != CAIRO_STATUS_SUCCESS) {
320 cairo_surface_destroy(t);
321 return NULL;
322 }
323 // Paint it in.
324 cairo_t *d = cairo_create(s2);
325 cairo_scale(d, scale, scale);
326 cairo_set_source_surface(d, t, 0, 0);
327 cairo_paint(d);
328 cairo_destroy(d);
329
330 cairo_surface_destroy(t);
331 return s2;
332}
337cairo_surface_t *x11_helper_get_screenshot_surface(void) {
338 return cairo_xcb_surface_create(xcb->connection, xcb_stuff_get_root_window(),
339 root_visual, xcb->screen->width_in_pixels,
340 xcb->screen->height_in_pixels);
341}
342
343static xcb_pixmap_t get_root_pixmap(xcb_connection_t *c, xcb_screen_t *screen,
344 xcb_atom_t atom) {
345 xcb_get_property_cookie_t cookie;
346 xcb_get_property_reply_t *reply;
347 xcb_pixmap_t rootpixmap = XCB_NONE;
348
349 cookie = xcb_get_property(c, 0, screen->root, atom, XCB_ATOM_PIXMAP, 0, 1);
350
351 reply = xcb_get_property_reply(c, cookie, NULL);
352
353 if (reply) {
354 if (xcb_get_property_value_length(reply) == sizeof(xcb_pixmap_t)) {
355 memcpy(&rootpixmap, xcb_get_property_value(reply), sizeof(xcb_pixmap_t));
356 }
357 free(reply);
358 }
359
360 return rootpixmap;
361}
362
363cairo_surface_t *x11_helper_get_bg_surface(void) {
364 xcb_pixmap_t pm =
365 get_root_pixmap(xcb->connection, xcb->screen, netatoms[ESETROOT_PMAP_ID]);
366 if (pm == XCB_NONE) {
367 return NULL;
368 }
369 return cairo_xcb_surface_create(xcb->connection, pm, root_visual,
370 xcb->screen->width_in_pixels,
371 xcb->screen->height_in_pixels);
372}
373
374// retrieve a text property from a window
375// technically we could use window_get_prop(), but this is better for character
376// set support
377char *window_get_text_prop(xcb_window_t w, xcb_atom_t atom) {
378 xcb_get_property_cookie_t c = xcb_get_property(
379 xcb->connection, 0, w, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
380 xcb_get_property_reply_t *r =
381 xcb_get_property_reply(xcb->connection, c, NULL);
382 if (r) {
383 if (xcb_get_property_value_length(r) > 0) {
384 char *str = NULL;
385 if (r->type == netatoms[UTF8_STRING]) {
386 str = g_strndup(xcb_get_property_value(r),
387 xcb_get_property_value_length(r));
388 } else if (r->type == netatoms[STRING]) {
389 str = rofi_latin_to_utf8_strdup(xcb_get_property_value(r),
390 xcb_get_property_value_length(r));
391 } else {
392 str = g_strdup("Invalid encoding.");
393 }
394
395 free(r);
396 return str;
397 }
398 free(r);
399 }
400 return NULL;
401}
402
403void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms,
404 int count) {
405 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE, w, prop,
406 XCB_ATOM_ATOM, 32, count, atoms);
407}
408
409/****
410 * Code used to get monitor layout.
411 */
412
416static void x11_monitor_free(workarea *m) {
417 g_free(m->name);
418 g_free(m);
419}
420
421static void x11_monitors_free(void) {
422 while (xcb->monitors != NULL) {
423 workarea *m = xcb->monitors;
424 xcb->monitors = m->next;
426 }
427}
428
435 double ratio_res = w->w / (double)w->h;
436 double ratio_size = w->mw / (double)w->mh;
437
438 if ((ratio_res < 1.0 && ratio_size > 1.0) ||
439 (ratio_res > 1.0 && ratio_size < 1.0)) {
440 // Oposite ratios, swap them.
441 int nh = w->mw;
442 w->mw = w->mh;
443 w->mh = nh;
444 }
445}
449static workarea *x11_get_monitor_from_output(xcb_randr_output_t out) {
450 xcb_randr_get_output_info_reply_t *op_reply;
451 xcb_randr_get_crtc_info_reply_t *crtc_reply;
452 xcb_randr_get_output_info_cookie_t it =
453 xcb_randr_get_output_info(xcb->connection, out, XCB_CURRENT_TIME);
454 op_reply = xcb_randr_get_output_info_reply(xcb->connection, it, NULL);
455 if (op_reply->crtc == XCB_NONE) {
456 free(op_reply);
457 return NULL;
458 }
459 xcb_randr_get_crtc_info_cookie_t ct = xcb_randr_get_crtc_info(
460 xcb->connection, op_reply->crtc, XCB_CURRENT_TIME);
461 crtc_reply = xcb_randr_get_crtc_info_reply(xcb->connection, ct, NULL);
462 if (!crtc_reply) {
463 free(op_reply);
464 return NULL;
465 }
466 workarea *retv = g_malloc0(sizeof(workarea));
467 retv->x = crtc_reply->x;
468 retv->y = crtc_reply->y;
469 retv->w = crtc_reply->width;
470 retv->h = crtc_reply->height;
471
472 retv->mw = op_reply->mm_width;
473 retv->mh = op_reply->mm_height;
475
476 char *tname = (char *)xcb_randr_get_output_info_name(op_reply);
477 int tname_len = xcb_randr_get_output_info_name_length(op_reply);
478
479 retv->name = g_malloc0((tname_len + 1) * sizeof(char));
480 memcpy(retv->name, tname, tname_len);
481
482 free(crtc_reply);
483 free(op_reply);
484 return retv;
485}
486
487#if (((XCB_RANDR_MAJOR_VERSION >= RANDR_PREF_MAJOR_VERSION) && \
488 (XCB_RANDR_MINOR_VERSION >= RANDR_PREF_MINOR_VERSION)) || \
489 XCB_RANDR_MAJOR_VERSION > RANDR_PREF_MAJOR_VERSION)
497static workarea *
498x11_get_monitor_from_randr_monitor(xcb_randr_monitor_info_t *mon) {
499 // Query to the name of the monitor.
500 xcb_generic_error_t *err;
501 xcb_get_atom_name_cookie_t anc =
502 xcb_get_atom_name(xcb->connection, mon->name);
503 xcb_get_atom_name_reply_t *atom_reply =
504 xcb_get_atom_name_reply(xcb->connection, anc, &err);
505 if (err != NULL) {
506 g_warning("Could not get RandR monitor name: X11 error code %d\n",
507 err->error_code);
508 free(err);
509 return NULL;
510 }
511 workarea *retv = g_malloc0(sizeof(workarea));
512
513 // Is primary monitor.
514 retv->primary = mon->primary;
515
516 // Position and size.
517 retv->x = mon->x;
518 retv->y = mon->y;
519 retv->w = mon->width;
520 retv->h = mon->height;
521
522 // Physical
523 retv->mw = mon->width_in_millimeters;
524 retv->mh = mon->height_in_millimeters;
526
527 // Name
528 retv->name =
529 g_strdup_printf("%.*s", xcb_get_atom_name_name_length(atom_reply),
530 xcb_get_atom_name_name(atom_reply));
531
532 // Free name atom.
533 free(atom_reply);
534
535 return retv;
536}
537#endif
538
539static int x11_is_extension_present(const char *extension) {
540 xcb_query_extension_cookie_t randr_cookie =
541 xcb_query_extension(xcb->connection, strlen(extension), extension);
542
543 xcb_query_extension_reply_t *randr_reply =
544 xcb_query_extension_reply(xcb->connection, randr_cookie, NULL);
545
546 int present = randr_reply->present;
547
548 free(randr_reply);
549
550 return present;
551}
552
554 xcb_xinerama_query_screens_cookie_t screens_cookie =
555 xcb_xinerama_query_screens_unchecked(xcb->connection);
556
557 xcb_xinerama_query_screens_reply_t *screens_reply =
558 xcb_xinerama_query_screens_reply(xcb->connection, screens_cookie, NULL);
559
560 xcb_xinerama_screen_info_iterator_t screens_iterator =
561 xcb_xinerama_query_screens_screen_info_iterator(screens_reply);
562
563 for (; screens_iterator.rem > 0;
564 xcb_xinerama_screen_info_next(&screens_iterator)) {
565 workarea *w = g_malloc0(sizeof(workarea));
566
567 w->x = screens_iterator.data->x_org;
568 w->y = screens_iterator.data->y_org;
569 w->w = screens_iterator.data->width;
570 w->h = screens_iterator.data->height;
571
572 w->next = xcb->monitors;
573 xcb->monitors = w;
574 }
575
576 int index = 0;
577 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
578 iter->monitor_id = index++;
579 }
580
581 free(screens_reply);
582}
583
585 if (xcb->monitors) {
586 return;
587 }
588 // If RANDR is not available, try Xinerama
589 if (!x11_is_extension_present("RANDR")) {
590 // Check if xinerama is available.
591 if (x11_is_extension_present("XINERAMA")) {
592 g_debug("Query XINERAMA for monitor layout.");
594 return;
595 }
596 g_debug("No RANDR or Xinerama available for getting monitor layout.");
597 return;
598 }
599 g_debug("Query RANDR for monitor layout.");
600
601 g_debug("Randr XCB api version: %d.%d.", XCB_RANDR_MAJOR_VERSION,
602 XCB_RANDR_MINOR_VERSION);
603#if (((XCB_RANDR_MAJOR_VERSION == RANDR_PREF_MAJOR_VERSION) && \
604 (XCB_RANDR_MINOR_VERSION >= RANDR_PREF_MINOR_VERSION)) || \
605 XCB_RANDR_MAJOR_VERSION > RANDR_PREF_MAJOR_VERSION)
606 xcb_randr_query_version_cookie_t cversion = xcb_randr_query_version(
608 xcb_randr_query_version_reply_t *rversion =
609 xcb_randr_query_version_reply(xcb->connection, cversion, NULL);
610 if (rversion) {
611 g_debug("Found randr version: %d.%d", rversion->major_version,
612 rversion->minor_version);
613 // Check if we are 1.5 and up.
614 if (((rversion->major_version == RANDR_PREF_MAJOR_VERSION) &&
615 (rversion->minor_version >= RANDR_PREF_MINOR_VERSION)) ||
616 (rversion->major_version > RANDR_PREF_MAJOR_VERSION)) {
617 xcb_randr_get_monitors_cookie_t t =
618 xcb_randr_get_monitors(xcb->connection, xcb->screen->root, 1);
619 xcb_randr_get_monitors_reply_t *mreply =
620 xcb_randr_get_monitors_reply(xcb->connection, t, NULL);
621 if (mreply) {
622 xcb_randr_monitor_info_iterator_t iter =
623 xcb_randr_get_monitors_monitors_iterator(mreply);
624 while (iter.rem > 0) {
625 workarea *w = x11_get_monitor_from_randr_monitor(iter.data);
626 if (w) {
627 w->next = xcb->monitors;
628 xcb->monitors = w;
629 }
630 xcb_randr_monitor_info_next(&iter);
631 }
632 free(mreply);
633 }
634 }
635 free(rversion);
636 }
637#endif
638
639 // If no monitors found.
640 if (xcb->monitors == NULL) {
641 xcb_randr_get_screen_resources_current_reply_t *res_reply;
642 xcb_randr_get_screen_resources_current_cookie_t src;
643 src = xcb_randr_get_screen_resources_current(xcb->connection,
644 xcb->screen->root);
645 res_reply = xcb_randr_get_screen_resources_current_reply(xcb->connection,
646 src, NULL);
647 if (!res_reply) {
648 return; // just report error
649 }
650 int mon_num =
651 xcb_randr_get_screen_resources_current_outputs_length(res_reply);
652 xcb_randr_output_t *ops =
653 xcb_randr_get_screen_resources_current_outputs(res_reply);
654
655 // Get primary.
656 xcb_randr_get_output_primary_cookie_t pc =
657 xcb_randr_get_output_primary(xcb->connection, xcb->screen->root);
658 xcb_randr_get_output_primary_reply_t *pc_rep =
659 xcb_randr_get_output_primary_reply(xcb->connection, pc, NULL);
660
661 for (int i = mon_num - 1; i >= 0; i--) {
663 if (w) {
664 w->next = xcb->monitors;
665 xcb->monitors = w;
666 if (pc_rep && pc_rep->output == ops[i]) {
667 w->primary = TRUE;
668 }
669 }
670 }
671 // If exists, free primary output reply.
672 if (pc_rep) {
673 free(pc_rep);
674 }
675 free(res_reply);
676 }
677
678 // Number monitor
679 int index = 0;
680 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
681 iter->monitor_id = index++;
682 }
683}
684
686 int is_term = isatty(fileno(stdout));
687 printf("Monitor layout:\n");
688 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
689 printf("%s ID%s: %d", (is_term) ? color_bold : "",
690 is_term ? color_reset : "", iter->monitor_id);
691 if (iter->primary) {
692 printf(" (primary)");
693 }
694 printf("\n");
695 printf("%s name%s: %s\n", (is_term) ? color_bold : "",
696 is_term ? color_reset : "", iter->name);
697 printf("%s position%s: %d,%d\n", (is_term) ? color_bold : "",
698 is_term ? color_reset : "", iter->x, iter->y);
699 printf("%s size%s: %d,%d\n", (is_term) ? color_bold : "",
700 is_term ? color_reset : "", iter->w, iter->h);
701 if (iter->mw > 0 && iter->mh > 0) {
702 printf("%s size%s: %dmm,%dmm dpi: %.0f,%.0f\n",
703 (is_term) ? color_bold : "", is_term ? color_reset : "", iter->mw,
704 iter->mh, iter->w * 25.4 / (double)iter->mw,
705 iter->h * 25.4 / (double)iter->mh);
706 }
707 printf("\n");
708 }
709}
710
712 GSpawnChildSetupFunc *child_setup,
713 gpointer *user_data) {
714 if (context == NULL) {
715 return;
716 }
717
718 SnLauncherContext *sncontext;
719
720 sncontext = sn_launcher_context_new(xcb->sndisplay, xcb->screen_nbr);
721
722 sn_launcher_context_set_name(sncontext, context->name);
723 sn_launcher_context_set_description(sncontext, context->description);
724 if (context->binary != NULL) {
725 sn_launcher_context_set_binary_name(sncontext, context->binary);
726 }
727 if (context->icon != NULL) {
728 sn_launcher_context_set_icon_name(sncontext, context->icon);
729 }
730 if (context->app_id != NULL) {
732 }
733 if (context->wmclass != NULL) {
734 sn_launcher_context_set_wmclass(sncontext, context->wmclass);
735 }
736
737 xcb_get_property_cookie_t c;
738 unsigned int current_desktop = 0;
739
740 c = xcb_ewmh_get_current_desktop(&xcb->ewmh, xcb->screen_nbr);
741 if (xcb_ewmh_get_current_desktop_reply(&xcb->ewmh, c, &current_desktop,
742 NULL)) {
743 sn_launcher_context_set_workspace(sncontext, current_desktop);
744 }
745
746 sn_launcher_context_initiate(sncontext, "rofi", context->command,
748
749 *child_setup = (GSpawnChildSetupFunc)sn_launcher_context_setup_child_process;
750 *user_data = sncontext;
751}
752
753static int monitor_get_dimension(int monitor_id, workarea *mon) {
754 memset(mon, 0, sizeof(workarea));
755 mon->w = xcb->screen->width_in_pixels;
756 mon->h = xcb->screen->height_in_pixels;
757
758 workarea *iter = NULL;
759 for (iter = xcb->monitors; iter; iter = iter->next) {
760 if (iter->monitor_id == monitor_id) {
761 *mon = *iter;
762 return TRUE;
763 }
764 }
765 return FALSE;
766}
767// find the dimensions of the monitor displaying point x,y
768static void monitor_dimensions(int x, int y, workarea *mon) {
769 if (mon == NULL) {
770 g_error("%s: mon == NULL", __FUNCTION__);
771 return;
772 }
773 memset(mon, 0, sizeof(workarea));
774 mon->w = xcb->screen->width_in_pixels;
775 mon->h = xcb->screen->height_in_pixels;
776
777 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
778 if (INTERSECT(x, y, iter->x, iter->y, iter->w, iter->h)) {
779 *mon = *iter;
780 break;
781 }
782 }
783}
784
795static int pointer_get(xcb_window_t root, int *x, int *y) {
796 *x = 0;
797 *y = 0;
798 xcb_query_pointer_cookie_t c = xcb_query_pointer(xcb->connection, root);
799 xcb_query_pointer_reply_t *r =
800 xcb_query_pointer_reply(xcb->connection, c, NULL);
801 if (r) {
802 *x = r->root_x;
803 *y = r->root_y;
804 free(r);
805 return TRUE;
806 }
807
808 return FALSE;
809}
810
811static int monitor_active_from_winid(xcb_drawable_t id, workarea *mon) {
812 if (mon == NULL) {
813 g_error("%s: mon == NULL", __FUNCTION__);
814 return FALSE;
815 }
816 xcb_window_t root = xcb->screen->root;
817 xcb_get_geometry_cookie_t c = xcb_get_geometry(xcb->connection, id);
818 xcb_get_geometry_reply_t *r =
819 xcb_get_geometry_reply(xcb->connection, c, NULL);
820 if (r) {
821 xcb_translate_coordinates_cookie_t ct =
822 xcb_translate_coordinates(xcb->connection, id, root, r->x, r->y);
823 xcb_translate_coordinates_reply_t *t =
824 xcb_translate_coordinates_reply(xcb->connection, ct, NULL);
825 if (t) {
826 // place the menu above the window
827 // if some window is focused, place menu above window, else fall
828 // back to selected monitor.
829 mon->x = t->dst_x - r->x;
830 mon->y = t->dst_y - r->y;
831 mon->w = r->width;
832 mon->h = r->height;
833 free(r);
834 free(t);
835 return TRUE;
836 }
837 free(r);
838 }
839 return FALSE;
840}
842 int retv = FALSE;
843 xcb_window_t active_window;
844 xcb_get_property_cookie_t awc;
845 if (mon == NULL) {
846 g_error("%s: mon == NULL", __FUNCTION__);
847 return retv;
848 }
849 awc = xcb_ewmh_get_active_window(&xcb->ewmh, xcb->screen_nbr);
850 if (!xcb_ewmh_get_active_window_reply(&xcb->ewmh, awc, &active_window,
851 NULL)) {
852 g_debug(
853 "Failed to get active window, falling back to mouse location (-5).");
854 return retv;
855 }
856 xcb_query_tree_cookie_t tree_cookie =
857 xcb_query_tree(xcb->connection, active_window);
858 xcb_query_tree_reply_t *tree_reply =
859 xcb_query_tree_reply(xcb->connection, tree_cookie, NULL);
860 if (!tree_reply) {
861 g_debug(
862 "Failed to get parent window, falling back to mouse location (-5).");
863 return retv;
864 }
865 // get geometry.
866 xcb_get_geometry_cookie_t c =
867 xcb_get_geometry(xcb->connection, active_window);
868 xcb_get_geometry_reply_t *r =
869 xcb_get_geometry_reply(xcb->connection, c, NULL);
870 if (!r) {
871 g_debug("Failed to get geometry of active window, falling back to mouse "
872 "location (-5).");
873 free(tree_reply);
874 return retv;
875 }
876 xcb_translate_coordinates_cookie_t ct = xcb_translate_coordinates(
877 xcb->connection, tree_reply->parent, r->root, r->x, r->y);
878 xcb_translate_coordinates_reply_t *t =
879 xcb_translate_coordinates_reply(xcb->connection, ct, NULL);
880 if (t) {
881 if (mon_id == -2) {
882 // place the menu above the window
883 // if some window is focused, place menu above window, else fall
884 // back to selected monitor.
885 mon->x = t->dst_x - r->x;
886 mon->y = t->dst_y - r->y;
887 mon->w = r->width;
888 mon->h = r->height;
889 retv = TRUE;
892 mon->x += r->x;
893 mon->y += r->y;
894 }
895 g_debug("mon pos: %d %d %d-%d", mon->x, mon->y, mon->w, mon->h);
896 } else if (mon_id == -4) {
897 g_debug("Find monitor at location: %d %d", t->dst_x, t->dst_y);
898 monitor_dimensions(t->dst_x, t->dst_y, mon);
899 g_debug("Monitor found pos: %d %d %d-%d", mon->x, mon->y, mon->w, mon->h);
900 retv = TRUE;
901 }
902 free(t);
903 } else {
904 g_debug("Failed to get translate position of active window, falling back "
905 "to mouse location (-5).");
906 }
907 free(r);
908 free(tree_reply);
909 return retv;
910}
911static int monitor_active_from_id(int mon_id, workarea *mon) {
912 xcb_window_t root = xcb->screen->root;
913 int x, y;
914 if (mon == NULL) {
915 g_error("%s: mon == NULL", __FUNCTION__);
916 return FALSE;
917 }
918 g_debug("Monitor id: %d", mon_id);
919 // At mouse position.
920 if (mon_id == -3) {
921 if (pointer_get(root, &x, &y)) {
922 monitor_dimensions(x, y, mon);
923 mon->x = x;
924 mon->y = y;
925 return TRUE;
926 }
927 }
928 // Focused monitor
929 else if (mon_id == -1) {
930 g_debug("rofi on current monitor");
931 // Get the current desktop.
932 unsigned int current_desktop = 0;
933 xcb_get_property_cookie_t gcdc;
934 gcdc = xcb_ewmh_get_current_desktop(&xcb->ewmh, xcb->screen_nbr);
935 if (xcb_ewmh_get_current_desktop_reply(&xcb->ewmh, gcdc, &current_desktop,
936 NULL)) {
937 g_debug("Found current desktop: %u", current_desktop);
938 xcb_get_property_cookie_t c =
939 xcb_ewmh_get_desktop_viewport(&xcb->ewmh, xcb->screen_nbr);
940 xcb_ewmh_get_desktop_viewport_reply_t vp;
941 if (xcb_ewmh_get_desktop_viewport_reply(&xcb->ewmh, c, &vp, NULL)) {
942 g_debug("Found %d number of desktops", vp.desktop_viewport_len);
943 if (current_desktop < vp.desktop_viewport_len) {
944 g_debug("Found viewport for desktop: %d %d",
945 vp.desktop_viewport[current_desktop].x,
946 vp.desktop_viewport[current_desktop].y);
947 monitor_dimensions(vp.desktop_viewport[current_desktop].x,
948 vp.desktop_viewport[current_desktop].y, mon);
949 g_debug("Found monitor @: %d %d %dx%d", mon->x, mon->y, mon->w,
950 mon->h);
951 xcb_ewmh_get_desktop_viewport_reply_wipe(&vp);
952 return TRUE;
953 }
954 g_debug("Viewport does not exist for current desktop: %d, falling "
955 "back to mouse location (-5)",
956 current_desktop);
957 xcb_ewmh_get_desktop_viewport_reply_wipe(&vp);
958 } else {
959 g_debug("Failed to get viewport for current desktop: %d, falling back "
960 "to mouse location (-5).",
961 current_desktop);
962 }
963 } else {
964 g_debug("Failed to get current desktop, falling back to mouse location "
965 "(-5).");
966 }
967 } else if (mon_id == -2 || mon_id == -4) {
968 if (monitor_active_from_id_focused(mon_id, mon)) {
969 return TRUE;
970 }
971 }
972 // Monitor that has mouse pointer.
973 else if (mon_id == -5) {
974 if (pointer_get(root, &x, &y)) {
975 monitor_dimensions(x, y, mon);
976 return TRUE;
977 }
978 // This is our give up point.
979 return FALSE;
980 }
981 g_debug("Failed to find monitor, fall back to monitor showing mouse.");
982 return monitor_active_from_id(-5, mon);
983}
984
985// determine which monitor holds the active window, or failing that the mouse
986// pointer
987
988gboolean mon_set = FALSE;
990 0,
991};
993 if (mon == NULL) {
994 g_error("%s: mon == NULL", __FUNCTION__);
995 return FALSE;
996 }
997 g_debug("Monitor active");
998 if (mon_set) {
999 *mon = mon_cache;
1000 return TRUE;
1001 }
1002 if (config.monitor != NULL) {
1003 g_debug("Monitor lookup by name : %s", config.monitor);
1004 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
1005 if (g_strcmp0(config.monitor, iter->name) == 0) {
1006 *mon = *iter;
1007 mon_cache = *mon;
1008 mon_set = TRUE;
1009 return TRUE;
1010 }
1011 }
1012 }
1013 g_debug("Monitor lookup by name failed: %s", config.monitor);
1014 // Grab primary.
1015 if (g_strcmp0(config.monitor, "primary") == 0) {
1016 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
1017 if (iter->primary) {
1018 *mon = *iter;
1019 mon_cache = *mon;
1020 mon_set = TRUE;
1021 return TRUE;
1022 }
1023 }
1024 }
1025 if (g_str_has_prefix(config.monitor, "wid:")) {
1026 char *end = NULL;
1027 xcb_drawable_t win = g_ascii_strtoll(config.monitor + 4, &end, 0);
1028 if (end != config.monitor) {
1029 if (monitor_active_from_winid(win, mon)) {
1030 mon_cache = *mon;
1031 mon_set = TRUE;
1032 return TRUE;
1033 }
1034 }
1035 }
1036 {
1037 // IF fail, fall back to classic mode.
1038 char *end = NULL;
1039 gint64 mon_id = g_ascii_strtoll(config.monitor, &end, 0);
1040 if (end != config.monitor) {
1041 if (mon_id >= 0) {
1042 if (monitor_get_dimension(mon_id, mon)) {
1043 mon_cache = *mon;
1044 mon_set = TRUE;
1045 return TRUE;
1046 }
1047 g_warning("Failed to find selected monitor.");
1048 } else {
1049 int val = monitor_active_from_id(mon_id, mon);
1050 mon_cache = *mon;
1051 mon_set = TRUE;
1052 return val;
1053 }
1054 }
1055 }
1056 // Fallback.
1057 monitor_dimensions(0, 0, mon);
1058 mon_cache = *mon;
1059 mon_set = TRUE;
1060 return FALSE;
1061}
1062
1070 xcb_selection_notify_event_t *xse) {
1071 if (xse->property == XCB_ATOM_NONE) {
1072 g_warning("Failed to convert selection");
1073 } else if (xse->property == xcb->ewmh.UTF8_STRING) {
1074 gchar *text = window_get_text_prop(xse->requestor, xcb->ewmh.UTF8_STRING);
1075 if (text != NULL && text[0] != '\0') {
1076 unsigned int dl = strlen(text);
1077 // Strip new line
1078 for (unsigned int i = 0; i < dl; i++) {
1079 if (text[i] == '\n') {
1080 text[i] = '\0';
1081 }
1082 }
1083 rofi_view_handle_text(state, text);
1084 }
1085 g_free(text);
1086 } else {
1087 g_warning("Failed");
1088 }
1089}
1090
1091static gboolean
1093 NkBindingsMouseButton *button) {
1094 switch (x11_button) {
1095 case 1:
1096 *button = NK_BINDINGS_MOUSE_BUTTON_PRIMARY;
1097 break;
1098 case 3:
1099 *button = NK_BINDINGS_MOUSE_BUTTON_SECONDARY;
1100 break;
1101 case 2:
1102 *button = NK_BINDINGS_MOUSE_BUTTON_MIDDLE;
1103 break;
1104 case 8:
1105 *button = NK_BINDINGS_MOUSE_BUTTON_BACK;
1106 break;
1107 case 9:
1108 *button = NK_BINDINGS_MOUSE_BUTTON_FORWARD;
1109 break;
1110 case 4:
1111 case 5:
1112 case 6:
1113 case 7:
1114 return FALSE;
1115 default:
1116 *button = NK_BINDINGS_MOUSE_BUTTON_EXTRA + x11_button;
1117 }
1118 return TRUE;
1119}
1120
1121static gboolean x11_button_to_nk_bindings_scroll(guint32 x11_button,
1122 NkBindingsScrollAxis *axis,
1123 gint32 *steps) {
1124 *steps = 1;
1125 switch (x11_button) {
1126 case 4:
1127 *steps = -1;
1128 /* fallthrough */
1129 case 5:
1130 *axis = NK_BINDINGS_SCROLL_AXIS_VERTICAL;
1131 break;
1132 case 6:
1133 *steps = -1;
1134 /* fallthrough */
1135 case 7:
1136 *axis = NK_BINDINGS_SCROLL_AXIS_HORIZONTAL;
1137 break;
1138 default:
1139 return FALSE;
1140 }
1141 return TRUE;
1142}
1143
1147static void main_loop_x11_event_handler_view(xcb_generic_event_t *event) {
1149 if (state == NULL) {
1150 return;
1151 }
1152
1153 switch (event->response_type & ~0x80) {
1154 case XCB_CLIENT_MESSAGE: {
1155 xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event;
1156 xcb_atom_t atom = cme->data.data32[0];
1157 xcb_timestamp_t time = cme->data.data32[1];
1158 if (atom == netatoms[WM_TAKE_FOCUS]) {
1159 xcb_set_input_focus(xcb->connection, XCB_INPUT_FOCUS_NONE, cme->window,
1160 time);
1161 xcb_flush(xcb->connection);
1162 }
1163 break;
1164 }
1165 case XCB_DESTROY_NOTIFY: {
1166 xcb_window_t win = ((xcb_destroy_notify_event_t *)event)->window;
1167 if (win != rofi_view_get_window()) {
1168#ifdef WINDOW_MODE
1169 window_client_handle_signal(win, FALSE);
1170#endif
1171 } else {
1172 g_main_loop_quit(xcb->main_loop);
1173 }
1174 break;
1175 }
1176 case XCB_CREATE_NOTIFY: {
1177 xcb_window_t win = ((xcb_create_notify_event_t *)event)->window;
1178 if (win != rofi_view_get_window()) {
1179#ifdef WINDOW_MODE
1180 window_client_handle_signal(win, TRUE);
1181#endif
1182 }
1183 break;
1184 }
1185 case XCB_EXPOSE:
1187 break;
1188 case XCB_CONFIGURE_NOTIFY: {
1189 xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *)event;
1191 break;
1192 }
1193 case XCB_MOTION_NOTIFY: {
1194 xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *)event;
1195 gboolean button_mask = xme->state & XCB_EVENT_MASK_BUTTON_1_MOTION;
1196 if (button_mask && config.click_to_exit == TRUE) {
1197 xcb->mouse_seen = TRUE;
1198 }
1199 rofi_view_handle_mouse_motion(state, xme->event_x, xme->event_y,
1200 !button_mask && config.hover_select);
1201 break;
1202 }
1203 case XCB_BUTTON_PRESS: {
1204 xcb_button_press_event_t *bpe = (xcb_button_press_event_t *)event;
1205 NkBindingsMouseButton button;
1206 NkBindingsScrollAxis axis;
1207 gint32 steps;
1208
1209 xcb->last_timestamp = bpe->time;
1210 rofi_view_handle_mouse_motion(state, bpe->event_x, bpe->event_y, FALSE);
1211 if (x11_button_to_nk_bindings_button(bpe->detail, &button)) {
1212 nk_bindings_seat_handle_button(xcb->bindings_seat, NULL, button,
1213 NK_BINDINGS_BUTTON_STATE_PRESS, bpe->time);
1214 } else if (x11_button_to_nk_bindings_scroll(bpe->detail, &axis, &steps)) {
1215 nk_bindings_seat_handle_scroll(xcb->bindings_seat, NULL, axis, steps);
1216 }
1217 break;
1218 }
1219 case XCB_BUTTON_RELEASE: {
1220 xcb_button_release_event_t *bre = (xcb_button_release_event_t *)event;
1221 NkBindingsMouseButton button;
1222
1223 xcb->last_timestamp = bre->time;
1224 if (x11_button_to_nk_bindings_button(bre->detail, &button)) {
1225 nk_bindings_seat_handle_button(xcb->bindings_seat, NULL, button,
1226 NK_BINDINGS_BUTTON_STATE_RELEASE,
1227 bre->time);
1228 }
1229 if (config.click_to_exit == TRUE) {
1230 if (!xcb->mouse_seen) {
1231 rofi_view_temp_click_to_exit(state, bre->event);
1232 }
1233 xcb->mouse_seen = FALSE;
1234 }
1235 break;
1236 }
1237 // Paste event.
1238 case XCB_SELECTION_NOTIFY:
1239 rofi_view_paste(state, (xcb_selection_notify_event_t *)event);
1240 break;
1241 case XCB_KEYMAP_NOTIFY: {
1242 xcb_keymap_notify_event_t *kne = (xcb_keymap_notify_event_t *)event;
1243 for (gint32 by = 0; by < 31; ++by) {
1244 for (gint8 bi = 0; bi < 7; ++bi) {
1245 if (kne->keys[by] & (1 << bi)) {
1246 // X11 keycodes starts at 8
1247 nk_bindings_seat_handle_key(xcb->bindings_seat, NULL,
1248 (8 * by + bi) + 8,
1249 NK_BINDINGS_KEY_STATE_PRESSED);
1250 }
1251 }
1252 }
1253 break;
1254 }
1255 case XCB_KEY_PRESS: {
1256 xcb_key_press_event_t *xkpe = (xcb_key_press_event_t *)event;
1257 gchar *text;
1258
1259 xcb->last_timestamp = xkpe->time;
1261 text = nk_bindings_seat_handle_key_with_modmask(
1262 xcb->bindings_seat, NULL, xkpe->state, xkpe->detail,
1263 NK_BINDINGS_KEY_STATE_PRESS);
1264 } else {
1265 text = nk_bindings_seat_handle_key(
1266 xcb->bindings_seat, NULL, xkpe->detail,
1267 NK_BINDINGS_KEY_STATE_PRESS);
1268 }
1269 if (text != NULL) {
1270 rofi_view_handle_text(state, text);
1271 g_free(text);
1272 }
1273 break;
1274 }
1275 case XCB_KEY_RELEASE: {
1276 xcb_key_release_event_t *xkre = (xcb_key_release_event_t *)event;
1277 xcb->last_timestamp = xkre->time;
1278 nk_bindings_seat_handle_key(xcb->bindings_seat, NULL, xkre->detail,
1279 NK_BINDINGS_KEY_STATE_RELEASE);
1280 break;
1281 }
1282 default:
1283 break;
1284 }
1286}
1287
1288static gboolean main_loop_x11_event_handler(xcb_generic_event_t *ev,
1289 G_GNUC_UNUSED gpointer user_data) {
1290 if (ev == NULL) {
1291 int status = xcb_connection_has_error(xcb->connection);
1292 if (status > 0) {
1293 g_warning("The XCB connection to X server had a fatal error: %d", status);
1294 g_main_loop_quit(xcb->main_loop);
1295 return G_SOURCE_REMOVE;
1296 }
1297 // DD: it seems this handler often gets dispatched while the queue in GWater is empty.
1298 // resulting in a NULL for ev. This seems not an error.
1299 //g_warning("main_loop_x11_event_handler: ev == NULL, status == %d", status);
1300 return G_SOURCE_CONTINUE;
1301 }
1302 uint8_t type = ev->response_type & ~0x80;
1303 if (type == xcb->xkb.first_event) {
1304 switch (ev->pad0) {
1305 case XCB_XKB_MAP_NOTIFY: {
1306 struct xkb_keymap *keymap = xkb_x11_keymap_new_from_device(
1307 nk_bindings_seat_get_context(xcb->bindings_seat), xcb->connection,
1308 xcb->xkb.device_id, 0);
1309 struct xkb_state *state = xkb_x11_state_new_from_device(
1310 keymap, xcb->connection, xcb->xkb.device_id);
1311 nk_bindings_seat_update_keymap(xcb->bindings_seat, keymap, state);
1312 xkb_keymap_unref(keymap);
1313 xkb_state_unref(state);
1314 break;
1315 }
1316 case XCB_XKB_STATE_NOTIFY: {
1317 xcb_xkb_state_notify_event_t *ksne = (xcb_xkb_state_notify_event_t *)ev;
1318 nk_bindings_seat_update_mask(xcb->bindings_seat, NULL, ksne->baseMods,
1319 ksne->latchedMods, ksne->lockedMods,
1320 ksne->baseGroup, ksne->latchedGroup,
1321 ksne->lockedGroup);
1323 break;
1324 }
1325 }
1326 return G_SOURCE_CONTINUE;
1327 }
1328 if (xcb->sndisplay != NULL) {
1329 sn_xcb_display_process_event(xcb->sndisplay, ev);
1330 }
1332 return G_SOURCE_CONTINUE;
1333}
1334
1335void rofi_xcb_set_input_focus(xcb_window_t w) {
1336 if (config.steal_focus != TRUE) {
1337 xcb->focus_revert = 0;
1338 return;
1339 }
1340 xcb_generic_error_t *error;
1341 xcb_get_input_focus_reply_t *freply;
1342 xcb_get_input_focus_cookie_t fcookie = xcb_get_input_focus(xcb->connection);
1343 freply = xcb_get_input_focus_reply(xcb->connection, fcookie, &error);
1344 if (error != NULL) {
1345 g_warning("Could not get input focus (error %d), will revert focus to best "
1346 "effort",
1347 error->error_code);
1348 free(error);
1349 xcb->focus_revert = 0;
1350 } else {
1351 xcb->focus_revert = freply->focus;
1352 }
1353 xcb_set_input_focus(xcb->connection, XCB_INPUT_FOCUS_POINTER_ROOT, w,
1354 XCB_CURRENT_TIME);
1355 xcb_flush(xcb->connection);
1356}
1357
1359 if (xcb->focus_revert == 0) {
1360 return;
1361 }
1362
1363 xcb_set_input_focus(xcb->connection, XCB_INPUT_FOCUS_POINTER_ROOT,
1364 xcb->focus_revert, XCB_CURRENT_TIME);
1365 xcb_flush(xcb->connection);
1366}
1367
1368static int take_pointer(xcb_window_t w, int iters) {
1369 int i = 0;
1370 while (TRUE) {
1371 if (xcb_connection_has_error(xcb->connection)) {
1372 g_warning("Connection has error");
1373 exit(EXIT_FAILURE);
1374 }
1375 xcb_grab_pointer_cookie_t cc =
1376 xcb_grab_pointer(xcb->connection, 1, w, XCB_EVENT_MASK_BUTTON_RELEASE,
1377 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, w, XCB_NONE,
1378 XCB_CURRENT_TIME);
1379 xcb_grab_pointer_reply_t *r =
1380 xcb_grab_pointer_reply(xcb->connection, cc, NULL);
1381 if (r) {
1382 if (r->status == XCB_GRAB_STATUS_SUCCESS) {
1383 free(r);
1384 return 1;
1385 }
1386 free(r);
1387 }
1388 if ((++i) > iters) {
1389 break;
1390 }
1391 struct timespec del = {.tv_sec = 0, .tv_nsec = 1000000};
1392 nanosleep(&del, NULL);
1393 }
1394 return 0;
1395}
1396
1397static int take_keyboard(xcb_window_t w, int iters) {
1398 int i = 0;
1399 while (TRUE) {
1400 if (xcb_connection_has_error(xcb->connection)) {
1401 g_warning("Connection has error");
1402 exit(EXIT_FAILURE);
1403 }
1404 xcb_grab_keyboard_cookie_t cc =
1405 xcb_grab_keyboard(xcb->connection, 1, w, XCB_CURRENT_TIME,
1406 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
1407 xcb_grab_keyboard_reply_t *r =
1408 xcb_grab_keyboard_reply(xcb->connection, cc, NULL);
1409 if (r) {
1410 if (r->status == XCB_GRAB_STATUS_SUCCESS) {
1411 free(r);
1412 return 1;
1413 }
1414 free(r);
1415 }
1416 if ((++i) > iters) {
1417 break;
1418 }
1419 struct timespec del = {.tv_sec = 0, .tv_nsec = 1000000};
1420 nanosleep(&del, NULL);
1421 }
1422 return 0;
1423}
1424
1425static void release_keyboard(void) {
1426 xcb_ungrab_keyboard(xcb->connection, XCB_CURRENT_TIME);
1427}
1428static void release_pointer(void) {
1429 xcb_ungrab_pointer(xcb->connection, XCB_CURRENT_TIME);
1430}
1431
1433static int error_trap_depth = 0;
1434static void error_trap_push(G_GNUC_UNUSED SnDisplay *display,
1435 G_GNUC_UNUSED xcb_connection_t *xdisplay) {
1437}
1438
1439static void error_trap_pop(G_GNUC_UNUSED SnDisplay *display,
1440 xcb_connection_t *xdisplay) {
1441 if (error_trap_depth == 0) {
1442 g_warning("Error trap underflow!");
1443 exit(EXIT_FAILURE);
1444 }
1445
1446 xcb_flush(xdisplay);
1448}
1449
1454 // X atom values
1455 for (int i = 0; i < NUM_NETATOMS; i++) {
1456 xcb_intern_atom_cookie_t cc = xcb_intern_atom(
1457 xcb->connection, 0, strlen(netatom_names[i]), netatom_names[i]);
1458 xcb_intern_atom_reply_t *r =
1459 xcb_intern_atom_reply(xcb->connection, cc, NULL);
1460 if (r) {
1461 netatoms[i] = r->atom;
1462 free(r);
1463 }
1464 }
1465}
1466
1468 xcb_window_t wm_win = 0;
1469 xcb_get_property_cookie_t cc = xcb_ewmh_get_supporting_wm_check_unchecked(
1471
1472 if (xcb_ewmh_get_supporting_wm_check_reply(&xcb->ewmh, cc, &wm_win, NULL)) {
1473 xcb_ewmh_get_utf8_strings_reply_t wtitle;
1474 xcb_get_property_cookie_t cookie =
1475 xcb_ewmh_get_wm_name_unchecked(&(xcb->ewmh), wm_win);
1476 if (xcb_ewmh_get_wm_name_reply(&(xcb->ewmh), cookie, &wtitle, (void *)0)) {
1477 if (wtitle.strings_len > 0) {
1478 g_debug("Found window manager: |%s|", wtitle.strings);
1479 if (g_strcmp0(wtitle.strings, "i3") == 0) {
1482 } else if (g_strcmp0(wtitle.strings, "bspwm") == 0) {
1484 }
1485 }
1486 xcb_ewmh_get_utf8_strings_reply_wipe(&wtitle);
1487 }
1488 }
1489}
1490
1491gboolean display_setup(GMainLoop *main_loop, NkBindings *bindings) {
1492 // Get DISPLAY, first env, then argument.
1493 // We never modify display_str content.
1494 char *display_str = (char *)g_getenv("DISPLAY");
1495 find_arg_str("-display", &display_str);
1496
1498 xcb->source = g_water_xcb_source_new(g_main_loop_get_context(xcb->main_loop),
1499 display_str, &xcb->screen_nbr,
1500 main_loop_x11_event_handler, NULL, NULL);
1501 if (xcb->source == NULL) {
1502 g_warning("Failed to open display: %s", display_str);
1503 return FALSE;
1504 }
1505 xcb->connection = g_water_xcb_source_get_connection(xcb->source);
1506
1507 TICK_N("Open Display");
1508
1509 xcb->screen = xcb_aux_get_screen(xcb->connection, xcb->screen_nbr);
1510
1512
1513 xcb_intern_atom_cookie_t *ac =
1514 xcb_ewmh_init_atoms(xcb->connection, &xcb->ewmh);
1515 xcb_generic_error_t *errors = NULL;
1516 xcb_ewmh_init_atoms_replies(&xcb->ewmh, ac, &errors);
1517 if (errors) {
1518 g_warning("Failed to create EWMH atoms");
1519 free(errors);
1520 }
1521 // Discover the current active window manager.
1523 TICK_N("Setup XCB");
1524
1525 if (xkb_x11_setup_xkb_extension(
1526 xcb->connection, XKB_X11_MIN_MAJOR_XKB_VERSION,
1527 XKB_X11_MIN_MINOR_XKB_VERSION, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
1528 NULL, NULL, &xcb->xkb.first_event, NULL) < 0) {
1529 g_warning("cannot setup XKB extension!");
1530 return FALSE;
1531 }
1532
1533 xcb->xkb.device_id = xkb_x11_get_core_keyboard_device_id(xcb->connection);
1534
1535 enum {
1536 required_events =
1537 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
1538 XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
1539
1540 required_nkn_details = (XCB_XKB_NKN_DETAIL_KEYCODES),
1541
1542 required_map_parts =
1543 (XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS |
1544 XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
1545 XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_VIRTUAL_MODS |
1546 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
1547
1548 required_state_details =
1549 (XCB_XKB_STATE_PART_MODIFIER_BASE | XCB_XKB_STATE_PART_MODIFIER_LATCH |
1550 XCB_XKB_STATE_PART_MODIFIER_LOCK | XCB_XKB_STATE_PART_GROUP_BASE |
1551 XCB_XKB_STATE_PART_GROUP_LATCH | XCB_XKB_STATE_PART_GROUP_LOCK),
1552 };
1553
1554 static const xcb_xkb_select_events_details_t details = {
1555 .affectNewKeyboard = required_nkn_details,
1556 .newKeyboardDetails = required_nkn_details,
1557 .affectState = required_state_details,
1558 .stateDetails = required_state_details,
1559 };
1560 xcb_xkb_select_events(xcb->connection, xcb->xkb.device_id,
1561 required_events, /* affectWhich */
1562 0, /* clear */
1563 required_events, /* selectAll */
1564 required_map_parts, /* affectMap */
1565 required_map_parts, /* map */
1566 &details);
1567
1568 xcb->bindings_seat = nk_bindings_seat_new(bindings, XKB_CONTEXT_NO_FLAGS);
1569 struct xkb_keymap *keymap = xkb_x11_keymap_new_from_device(
1570 nk_bindings_seat_get_context(xcb->bindings_seat), xcb->connection,
1571 xcb->xkb.device_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
1572 if (keymap == NULL) {
1573 g_warning("Failed to get Keymap for current keyboard device.");
1574 return FALSE;
1575 }
1576 struct xkb_state *state = xkb_x11_state_new_from_device(
1577 keymap, xcb->connection, xcb->xkb.device_id);
1578 if (state == NULL) {
1579 g_warning("Failed to get state object for current keyboard device.");
1580 return FALSE;
1581 }
1582
1583 nk_bindings_seat_update_keymap(xcb->bindings_seat, keymap, state);
1584 xkb_state_unref(state);
1585 xkb_keymap_unref(keymap);
1586
1587 // determine numlock mask so we can bind on keys with and without it
1589
1590 if (xcb_connection_has_error(xcb->connection)) {
1591 g_warning("Connection has error");
1592 return FALSE;
1593 }
1594
1595 uint32_t val[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
1596
1597 xcb_change_window_attributes(xcb->connection, xcb_stuff_get_root_window(),
1598 XCB_CW_EVENT_MASK, val);
1599
1600 // startup not.
1601 xcb->sndisplay =
1602 sn_xcb_display_new(xcb->connection, error_trap_push, error_trap_pop);
1603 if (xcb_connection_has_error(xcb->connection)) {
1604 g_warning("Connection has error");
1605 return FALSE;
1606 }
1607
1608 if (xcb->sndisplay != NULL) {
1609 xcb->sncontext = sn_launchee_context_new_from_environment(xcb->sndisplay,
1610 xcb->screen_nbr);
1611 }
1612 if (xcb_connection_has_error(xcb->connection)) {
1613 g_warning("Connection has error");
1614 return FALSE;
1615 }
1616
1617 return TRUE;
1618}
1619
1621 xcb_depth_t *root_depth = NULL;
1622 xcb_depth_iterator_t depth_iter;
1623 for (depth_iter = xcb_screen_allowed_depths_iterator(xcb->screen);
1624 depth_iter.rem; xcb_depth_next(&depth_iter)) {
1625 xcb_depth_t *d = depth_iter.data;
1626
1627 xcb_visualtype_iterator_t visual_iter;
1628 for (visual_iter = xcb_depth_visuals_iterator(d); visual_iter.rem;
1629 xcb_visualtype_next(&visual_iter)) {
1630 xcb_visualtype_t *v = visual_iter.data;
1631 if ((v->bits_per_rgb_value == 8) && (d->depth == 32) &&
1632 (v->_class == XCB_VISUAL_CLASS_TRUE_COLOR)) {
1633 depth = d;
1634 visual = v;
1635 }
1636 if (xcb->screen->root_visual == v->visual_id) {
1637 root_depth = d;
1638 root_visual = v;
1639 }
1640 }
1641 }
1642 if (visual != NULL) {
1643 xcb_void_cookie_t c;
1644 xcb_generic_error_t *e;
1645 map = xcb_generate_id(xcb->connection);
1646 c = xcb_create_colormap_checked(xcb->connection, XCB_COLORMAP_ALLOC_NONE,
1647 map, xcb->screen->root, visual->visual_id);
1648 e = xcb_request_check(xcb->connection, c);
1649 if (e) {
1650 depth = NULL;
1651 visual = NULL;
1652 free(e);
1653 }
1654 }
1655
1656 if (visual == NULL) {
1657 depth = root_depth;
1659 map = xcb->screen->default_colormap;
1660 }
1661}
1662
1663static void x11_lookup_cursors(void) {
1664 xcb_cursor_context_t *ctx;
1665
1666 if (xcb_cursor_context_new(xcb->connection, xcb->screen, &ctx) < 0) {
1667 return;
1668 }
1669
1670 for (int i = 0; i < NUM_CURSORS; ++i) {
1671 cursors[i] = xcb_cursor_load_cursor(ctx, cursor_names[i].css_name);
1672
1673 if (cursors[i] == XCB_CURSOR_NONE) {
1674 cursors[i] =
1675 xcb_cursor_load_cursor(ctx, cursor_names[i].traditional_name);
1676 }
1677 }
1678
1679 xcb_cursor_context_free(ctx);
1680}
1681
1686static gboolean lazy_grab_pointer(G_GNUC_UNUSED gpointer data) {
1687 // After 5 sec.
1688 if (lazy_grab_retry_count_pt > (5 * 1000)) {
1689 g_warning("Failed to grab pointer after %u times. Giving up.",
1691 return G_SOURCE_REMOVE;
1692 }
1694 return G_SOURCE_REMOVE;
1695 }
1697 return G_SOURCE_CONTINUE;
1698}
1699static gboolean lazy_grab_keyboard(G_GNUC_UNUSED gpointer data) {
1700 // After 5 sec.
1701 if (lazy_grab_retry_count_kb > (5 * 1000)) {
1702 g_warning("Failed to grab keyboard after %u times. Giving up.",
1704 g_main_loop_quit(xcb->main_loop);
1705 return G_SOURCE_REMOVE;
1706 }
1708 return G_SOURCE_REMOVE;
1709 }
1711 return G_SOURCE_CONTINUE;
1712}
1713
1714gboolean display_late_setup(void) {
1716
1718
1722 // Try to grab the keyboard as early as possible.
1723 // We grab this using the rootwindow (as dmenu does it).
1724 // this seems to result in the smallest delay for most people.
1725 if (find_arg("-normal-window") >= 0) {
1726 return TRUE;
1727 }
1728 if (find_arg("-no-lazy-grab") >= 0) {
1730 g_warning("Failed to grab keyboard, even after %d uS.", 500 * 1000);
1731 return FALSE;
1732 }
1734 g_warning("Failed to grab mouse pointer, even after %d uS.", 100 * 1000);
1735 }
1736 } else {
1738 g_timeout_add(1, lazy_grab_keyboard, NULL);
1739 }
1741 g_timeout_add(1, lazy_grab_pointer, NULL);
1742 }
1743 }
1744 return TRUE;
1745}
1746
1747xcb_window_t xcb_stuff_get_root_window(void) { return xcb->screen->root; }
1748
1752 xcb_flush(xcb->connection);
1753}
1754
1756 if (xcb->connection == NULL) {
1757 return;
1758 }
1759
1760 g_debug("Cleaning up XCB and XKB");
1761
1762 nk_bindings_seat_free(xcb->bindings_seat);
1763 if (xcb->sncontext != NULL) {
1764 sn_launchee_context_unref(xcb->sncontext);
1765 xcb->sncontext = NULL;
1766 }
1767 if (xcb->sndisplay != NULL) {
1768 sn_display_unref(xcb->sndisplay);
1769 xcb->sndisplay = NULL;
1770 }
1772 xcb_ewmh_connection_wipe(&(xcb->ewmh));
1773 xcb_flush(xcb->connection);
1774 xcb_aux_sync(xcb->connection);
1775 g_water_xcb_source_free(xcb->source);
1776 xcb->source = NULL;
1777 xcb->connection = NULL;
1778 xcb->screen = NULL;
1779 xcb->screen_nbr = 0;
1780}
1781
1782void x11_disable_decoration(xcb_window_t window) {
1783 // Flag used to indicate we are setting the decoration type.
1784 const uint32_t MWM_HINTS_DECORATIONS = (1 << 1);
1785 // Motif property data structure
1786 struct MotifWMHints {
1787 uint32_t flags;
1788 uint32_t functions;
1789 uint32_t decorations;
1790 int32_t inputMode;
1791 uint32_t state;
1792 };
1793
1794 struct MotifWMHints hints;
1795 hints.flags = MWM_HINTS_DECORATIONS;
1796 hints.decorations = 0;
1797 hints.functions = 0;
1798 hints.inputMode = 0;
1799 hints.state = 0;
1800
1801 xcb_atom_t ha = netatoms[_MOTIF_WM_HINTS];
1802 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE, window, ha, ha,
1803 32, 5, &hints);
1804}
1805
1806void x11_set_cursor(xcb_window_t window, X11CursorType type) {
1807 if (type < 0 || type >= NUM_CURSORS) {
1808 return;
1809 }
1810
1811 if (cursors[type] == XCB_CURSOR_NONE) {
1812 return;
1813 }
1814
1815 xcb_change_window_attributes(xcb->connection, window, XCB_CW_CURSOR,
1816 &(cursors[type]));
1817}
char * rofi_latin_to_utf8_strdup(const char *input, gssize length)
Definition helper.c:808
int find_arg_str(const char *const key, char **val)
Definition helper.c:311
int find_arg(const char *const key)
Definition helper.c:302
#define color_reset
Definition rofi.h:107
#define color_bold
Definition rofi.h:109
#define TICK()
Definition timings.h:64
#define TICK_N(a)
Definition timings.h:69
xcb_window_t rofi_view_get_window(void)
Definition view.c:2375
RofiViewState * rofi_view_get_active(void)
Definition view.c:549
void rofi_view_handle_text(RofiViewState *state, char *text)
Definition view.c:1643
void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y, gboolean find_mouse_target)
Definition view.c:1685
void rofi_view_temp_click_to_exit(RofiViewState *state, xcb_window_t target)
Definition view.c:1774
void rofi_view_temp_configure_notify(RofiViewState *state, xcb_configure_notify_event_t *xce)
Definition view.c:1740
void rofi_view_frame_callback(void)
Definition view.c:1783
void rofi_view_maybe_update(RofiViewState *state)
Definition view.c:1713
NkBindings * bindings
Definition rofi.c:133
GMainLoop * main_loop
Definition rofi.c:136
Settings config
const gchar * binary
Definition helper.h:290
const gchar * wmclass
Definition helper.h:298
const gchar * app_id
Definition helper.h:296
const gchar * description
Definition helper.h:292
const gchar * name
Definition helper.h:288
const gchar * icon
Definition helper.h:294
const gchar * command
Definition helper.h:300
gboolean xserver_i300_workaround
Definition settings.h:186
gboolean steal_focus
Definition settings.h:177
int click_to_exit
Definition settings.h:148
gboolean hover_select
Definition settings.h:122
char * monitor
Definition settings.h:137
int w
Definition xcb.h:104
int x
Definition xcb.h:100
int monitor_id
Definition xcb.h:96
char * name
Definition xcb.h:109
int mh
Definition xcb.h:107
struct _workarea * next
Definition xcb.h:111
int h
Definition xcb.h:106
int mw
Definition xcb.h:107
int primary
Definition xcb.h:98
int y
Definition xcb.h:102
GWaterXcbSource * source
xcb_connection_t * connection
uint8_t first_event
SnLauncheeContext * sncontext
int32_t device_id
struct _workarea * monitors
xcb_timestamp_t last_timestamp
xcb_ewmh_connection_t ewmh
gboolean mouse_seen
xcb_screen_t * screen
struct _xcb_stuff::@8 xkb
NkBindingsSeat * bindings_seat
GMainLoop * main_loop
xcb_window_t focus_revert
SnDisplay * sndisplay
GTimer * time
Definition view.c:239
workarea mon
Definition view.c:111
MenuFlags flags
Definition view.c:107
unsigned long long count
Definition view.c:120
xcb_colormap_t map
Definition xcb.c:98
gboolean display_late_setup(void)
Definition xcb.c:1714
static int take_pointer(xcb_window_t w, int iters)
Definition xcb.c:1368
int monitor_active(workarea *mon)
Definition xcb.c:992
const struct @4 cursor_names[]
static void x11_build_monitor_layout_xinerama()
Definition xcb.c:553
#define RANDR_PREF_MAJOR_VERSION
Definition xcb.c:72
const char * traditional_name
Definition xcb.c:117
void display_early_cleanup(void)
Definition xcb.c:1749
static int x11_is_extension_present(const char *extension)
Definition xcb.c:539
static gboolean lazy_grab_pointer(G_GNUC_UNUSED gpointer data)
Definition xcb.c:1686
static void x11_workarea_fix_rotation(workarea *w)
Definition xcb.c:434
void display_startup_notification(RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data)
Definition xcb.c:711
cairo_surface_t * x11_helper_get_screenshot_surface(void)
Definition xcb.c:337
static xcb_visualtype_t * lookup_visual(xcb_screen_t *s, xcb_visualid_t visual)
Definition xcb.c:121
static void x11_build_monitor_layout()
Definition xcb.c:584
char * window_get_text_prop(xcb_window_t w, xcb_atom_t atom)
Definition xcb.c:377
void display_cleanup(void)
Definition xcb.c:1755
xcb_stuff * xcb
Definition xcb.c:91
static xcb_pixmap_t get_root_pixmap(xcb_connection_t *c, xcb_screen_t *screen, xcb_atom_t atom)
Definition xcb.c:343
static void release_pointer(void)
Definition xcb.c:1428
struct _xcb_stuff xcb_int
Definition xcb.c:85
xcb_depth_t * depth
Definition xcb.c:96
static uint32_t * create_kernel(double radius, double deviation, uint32_t *sum2)
Definition xcb.c:139
static int monitor_active_from_winid(xcb_drawable_t id, workarea *mon)
Definition xcb.c:811
static void x11_create_visual_and_colormap(void)
Definition xcb.c:1620
static gboolean x11_button_to_nk_bindings_scroll(guint32 x11_button, NkBindingsScrollAxis *axis, gint32 *steps)
Definition xcb.c:1121
static void monitor_dimensions(int x, int y, workarea *mon)
Definition xcb.c:768
void rofi_xcb_revert_input_focus(void)
Definition xcb.c:1358
cairo_surface_t * x11_helper_get_bg_surface(void)
Definition xcb.c:363
static void x11_lookup_cursors(void)
Definition xcb.c:1663
void rofi_xcb_set_input_focus(xcb_window_t w)
Definition xcb.c:1335
static gboolean lazy_grab_keyboard(G_GNUC_UNUSED gpointer data)
Definition xcb.c:1699
#define INTERSECT(x, y, x1, y1, w1, h1)
Definition xcb.c:77
static workarea * x11_get_monitor_from_output(xcb_randr_output_t out)
Definition xcb.c:449
static void error_trap_pop(G_GNUC_UNUSED SnDisplay *display, xcb_connection_t *xdisplay)
Definition xcb.c:1439
void x11_set_cursor(xcb_window_t window, X11CursorType type)
Definition xcb.c:1806
workarea mon_cache
Definition xcb.c:989
unsigned int lazy_grab_retry_count_pt
Definition xcb.c:1685
static xcb_visualtype_t * root_visual
Definition xcb.c:102
static void x11_create_frequently_used_atoms(void)
Definition xcb.c:1453
static int monitor_active_from_id_focused(int mon_id, workarea *mon)
Definition xcb.c:841
static gboolean x11_button_to_nk_bindings_button(guint32 x11_button, NkBindingsMouseButton *button)
Definition xcb.c:1092
xcb_cursor_t cursors[NUM_CURSORS]
Definition xcb.c:109
static void x11_monitor_free(workarea *m)
Definition xcb.c:416
static void x11_helper_discover_window_manager(void)
Definition xcb.c:1467
static int monitor_get_dimension(int monitor_id, workarea *mon)
Definition xcb.c:753
static int take_keyboard(xcb_window_t w, int iters)
Definition xcb.c:1397
xcb_window_t xcb_stuff_get_root_window(void)
Definition xcb.c:1747
gboolean mon_set
Definition xcb.c:988
unsigned int lazy_grab_retry_count_kb
Definition xcb.c:1683
static void rofi_view_paste(RofiViewState *state, xcb_selection_notify_event_t *xse)
Definition xcb.c:1069
const char * css_name
Definition xcb.c:115
void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms, int count)
Definition xcb.c:403
const char * netatom_names[]
Definition xcb.c:104
void cairo_image_surface_blur(cairo_surface_t *surface, double radius, double deviation)
Definition xcb.c:167
static int monitor_active_from_id(int mon_id, workarea *mon)
Definition xcb.c:911
static void x11_monitors_free(void)
Definition xcb.c:421
gboolean display_setup(GMainLoop *main_loop, NkBindings *bindings)
Definition xcb.c:1491
void display_dump_monitor_layout(void)
Definition xcb.c:685
static void error_trap_push(G_GNUC_UNUSED SnDisplay *display, G_GNUC_UNUSED xcb_connection_t *xdisplay)
Definition xcb.c:1434
WindowManagerQuirk current_window_manager
Definition xcb.c:80
static int pointer_get(xcb_window_t root, int *x, int *y)
Definition xcb.c:795
#define RANDR_PREF_MINOR_VERSION
Definition xcb.c:74
#define sn_launcher_context_set_application_id
Definition xcb.c:56
static int error_trap_depth
Definition xcb.c:1433
static void release_keyboard(void)
Definition xcb.c:1425
static gboolean main_loop_x11_event_handler(xcb_generic_event_t *ev, G_GNUC_UNUSED gpointer user_data)
Definition xcb.c:1288
void x11_disable_decoration(xcb_window_t window)
Definition xcb.c:1782
static void main_loop_x11_event_handler_view(xcb_generic_event_t *event)
Definition xcb.c:1147
xcb_atom_t netatoms[NUM_NETATOMS]
Definition xcb.c:103
xcb_visualtype_t * visual
Definition xcb.c:97
cairo_surface_t * x11_helper_get_screenshot_surface_window(xcb_window_t window, int size)
Definition xcb.c:276
#define EWMH_ATOMS(X)
Definition xcb.h:79
#define ATOM_CHAR(x)
Definition xcb.h:76
WindowManagerQuirk
Definition xcb.h:195
@ WM_PANGO_WORKSPACE_NAMES
Definition xcb.h:201
@ WM_DO_NOT_CHANGE_CURRENT_DESKTOP
Definition xcb.h:199
@ WM_EWHM
Definition xcb.h:197
@ WM_ROOT_WINDOW_OFFSET
Definition xcb.h:203
X11CursorType
Definition xcb.h:174
@ NUM_CURSORS
Definition xcb.h:181
@ NUM_NETATOMS
Definition xcb.h:85