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