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 
85 struct _xcb_stuff xcb_int = {.connection = NULL,
86  .screen = NULL,
87  .screen_nbr = -1,
88  .sndisplay = NULL,
89  .sncontext = NULL,
90  .monitors = NULL};
92 
96 xcb_depth_t *depth = NULL;
97 xcb_visualtype_t *visual = NULL;
98 xcb_colormap_t map = XCB_COLORMAP_NONE;
102 static xcb_visualtype_t *root_visual = NULL;
103 xcb_atom_t netatoms[NUM_NETATOMS];
104 const char *netatom_names[] = {EWMH_ATOMS(ATOM_CHAR)};
105 
109 xcb_cursor_t cursors[NUM_CURSORS] = {XCB_CURSOR_NONE, XCB_CURSOR_NONE,
110  XCB_CURSOR_NONE};
111 
113 const struct {
115  const char *css_name;
117  const char *traditional_name;
118 } cursor_names[] = {
119  {"default", "left_ptr"}, {"pointer", "hand"}, {"text", "xterm"}};
120 
121 static 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 
139 static 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 
167 void 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 
276 cairo_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 }
337 cairo_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 
343 static 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 
363 cairo_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
377 char *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 
403 void 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 
416 static void x11_monitor_free(workarea *m) {
417  g_free(m->name);
418  g_free(m);
419 }
420 
421 static void x11_monitors_free(void) {
422  while (xcb->monitors != NULL) {
423  workarea *m = xcb->monitors;
424  xcb->monitors = m->next;
425  x11_monitor_free(m);
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 }
449 static 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)
497 static workarea *
498 x11_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 
539 static 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) {
731  sn_launcher_context_set_application_id(sncontext, context->app_id);
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 
753 static 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
768 static 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 
795 static 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 
811 static 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 }
841 static int monitor_active_from_id_focused(int mon_id, workarea *mon) {
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 }
911 static 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 
988 gboolean 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 
1069 static void rofi_view_paste(RofiViewState *state,
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 
1091 static 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 
1121 static 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 
1147 static 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;
1190  rofi_view_temp_configure_notify(state, xce);
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  }
1285  rofi_view_maybe_update(state);
1286 }
1287 
1288 static 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 
1335 void 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 
1368 static 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 
1397 static 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 
1425 static void release_keyboard(void) {
1426  xcb_ungrab_keyboard(xcb->connection, XCB_CURRENT_TIME);
1427 }
1428 static void release_pointer(void) {
1429  xcb_ungrab_pointer(xcb->connection, XCB_CURRENT_TIME);
1430 }
1431 
1433 static int error_trap_depth = 0;
1434 static void error_trap_push(G_GNUC_UNUSED SnDisplay *display,
1435  G_GNUC_UNUSED xcb_connection_t *xdisplay) {
1436  ++error_trap_depth;
1437 }
1438 
1439 static 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);
1447  --error_trap_depth;
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 
1491 gboolean 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 
1497  xcb->main_loop = main_loop;
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;
1658  visual = root_visual;
1659  map = xcb->screen->default_colormap;
1660  }
1661 }
1662 
1663 static 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 
1683 unsigned int lazy_grab_retry_count_kb = 0;
1685 unsigned int lazy_grab_retry_count_pt = 0;
1686 static 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 }
1699 static 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 
1714 gboolean 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  }
1733  if (!take_pointer(xcb_stuff_get_root_window(), 100)) {
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 
1747 xcb_window_t xcb_stuff_get_root_window(void) { return xcb->screen->root; }
1748 
1750  release_keyboard();
1751  release_pointer();
1752  xcb_flush(xcb->connection);
1753 }
1754 
1755 void display_cleanup(void) {
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 
1782 void 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 
1806 void 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
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: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
cairo_surface_t * x11_helper_get_screenshot_surface(void)
Definition: xcb.c:337
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
static void x11_build_monitor_layout()
Definition: xcb.c:584
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 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
static uint32_t * create_kernel(double radius, double deviation, uint32_t *sum2)
Definition: xcb.c:139
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
static xcb_visualtype_t * lookup_visual(xcb_screen_t *s, xcb_visualid_t visual)
Definition: xcb.c:121
#define INTERSECT(x, y, x1, y1, w1, h1)
Definition: xcb.c:77
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
cairo_surface_t * x11_helper_get_bg_surface(void)
Definition: xcb.c:363
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
cairo_surface_t * x11_helper_get_screenshot_surface_window(xcb_window_t window, int size)
Definition: xcb.c:276
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
char * window_get_text_prop(xcb_window_t w, xcb_atom_t atom)
Definition: xcb.c:377
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
static workarea * x11_get_monitor_from_output(xcb_randr_output_t out)
Definition: xcb.c:449
xcb_atom_t netatoms[NUM_NETATOMS]
Definition: xcb.c:103
xcb_visualtype_t * visual
Definition: xcb.c:97
#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