rofi 2.1.0
view.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2013-2020 Qball Cow <qball@gmpclient.org>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 */
27
29#define G_LOG_DOMAIN "View"
30
31#include <config.h>
32
33#include <errno.h>
34#include <locale.h>
35#include <signal.h>
36#include <stdint.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <time.h>
41#include <unistd.h>
42#ifdef XCB_IMDKIT
43#include <xcb-imdkit/encoding.h>
44#endif
45#include <xcb/xcb_ewmh.h>
46#include <xcb/xcb_icccm.h>
47#include <xcb/xkb.h>
48#include <xkbcommon/xkbcommon-x11.h>
49
50#include <cairo-xcb.h>
51#include <cairo.h>
52#include <gio/gio.h>
53
55#define SN_API_NOT_YET_FROZEN
56#include "rofi.h"
57#include <libsn/sn.h>
58
59#include "settings.h"
60#include "timings.h"
61
62#include "display.h"
63#include "helper-theme.h"
64#include "helper.h"
65#include "mode.h"
66#include "modes/modes.h"
67#include "xcb-internal.h"
68#include "xrmoptions.h"
69
70#include "view-internal.h"
71#include "view.h"
72
73#include "theme.h"
74
75#include "xcb.h"
76
78
79static void xcb_rofi_view_set_window_title(const char *title);
80
81static void xcb_rofi_view_queue_redraw(void);
82
83#ifdef XCB_IMDKIT
84static void xim_commit_string(xcb_xim_t *im, G_GNUC_UNUSED xcb_xic_t ic,
85 G_GNUC_UNUSED uint32_t flag, char *str,
86 uint32_t length, G_GNUC_UNUSED uint32_t *keysym,
87 G_GNUC_UNUSED size_t nKeySym,
88 G_GNUC_UNUSED void *user_data);
89static void xim_disconnected(G_GNUC_UNUSED xcb_xim_t *im,
90 G_GNUC_UNUSED void *user_data);
91xcb_xim_im_callback xim_callback = {.forward_event =
92 x11_event_handler_fowarding,
93 .commit_string = xim_commit_string,
94 .disconnected = xim_disconnected};
95#endif
96
98extern GThreadPool *tpool;
99
103static struct {
105 cairo_surface_t *fake_bg;
107 xcb_gcontext_t gc;
109 xcb_pixmap_t edit_pixmap;
111 cairo_surface_t *edit_surf;
113 cairo_t *edit_draw;
121 unsigned long long count;
125 gboolean fullscreen;
128} XcbState = {
129 .fake_bg = NULL,
130 .edit_surf = NULL,
131 .edit_draw = NULL,
132 .fake_bgrel = FALSE,
133 .idle_timeout = 0,
134 .count = 0L,
135 .repaint_source = 0,
136 .fullscreen = FALSE,
138
139static void xcb_rofi_view_get_current_monitor(int *width, int *height) {
140 if (width) {
141 *width = XcbState.mon.w;
142 }
143 if (height) {
144 *height = XcbState.mon.h;
145 }
146}
147
152gboolean do_bench = TRUE;
153
157static struct {
159 GTimer *time;
161 uint64_t draws;
163 double last_ts;
165 double min;
166} BenchMark = {.time = NULL, .draws = 0, .last_ts = 0.0, .min = G_MAXDOUBLE};
167
168static gboolean bench_update(void) {
169 if (!config.benchmark_ui) {
170 return FALSE;
171 }
172 BenchMark.draws++;
173 if (BenchMark.time == NULL) {
174 BenchMark.time = g_timer_new();
175 }
176
177 if ((BenchMark.draws & 1023) == 0) {
178 double ts = g_timer_elapsed(BenchMark.time, NULL);
179 double fps = 1024 / (ts - BenchMark.last_ts);
180
181 if (fps < BenchMark.min) {
182 BenchMark.min = fps;
183 }
184 printf("current: %.2f fps, avg: %.2f fps, min: %.2f fps, %lu draws\r\n",
185 fps, BenchMark.draws / ts, BenchMark.min, BenchMark.draws);
186
187 BenchMark.last_ts = ts;
188 }
189 return TRUE;
190}
191
192static gboolean xcb_rofi_view_repaint(G_GNUC_UNUSED void *data) {
194 if (state) {
195 // Repaint the view (if needed).
196 // After a resize the edit_pixmap surface might not contain anything
197 // anymore. If we already re-painted, this does nothing.
198
199 TICK_N("Update start");
200 rofi_view_update(state, FALSE);
201 g_debug("expose event");
202 TICK_N("Expose");
203 xcb_copy_area(xcb->connection, XcbState.edit_pixmap, CacheState.main_window,
204 XcbState.gc, 0, 0, 0, 0, state->width, state->height);
205 xcb_flush(xcb->connection);
206 TICK_N("flush");
207 XcbState.repaint_source = 0;
208 }
209 return (bench_update() == TRUE) ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
210}
211
218static void xcb_rofi_view_update(RofiViewState *state, gboolean qr) {
219 if (!widget_need_redraw(WIDGET(state->main_window))) {
220 return;
221 }
222 g_debug("Redraw view");
223 TICK();
224 cairo_t *d = XcbState.edit_draw;
225 cairo_set_operator(d, CAIRO_OPERATOR_SOURCE);
226 if (XcbState.fake_bg != NULL) {
227 if (XcbState.fake_bgrel) {
228 cairo_set_source_surface(d, XcbState.fake_bg, 0.0, 0.0);
229 } else {
230 cairo_set_source_surface(d, XcbState.fake_bg,
231 (double)(XcbState.mon.x - state->x),
232 (double)(XcbState.mon.y - state->y));
233 }
234 } else {
235 // Paint the background transparent.
236 cairo_set_source_rgba(d, 0, 0, 0, 0.0);
237 }
238 cairo_paint(d);
239
240 // Always paint as overlay over the background.
241 cairo_set_operator(d, CAIRO_OPERATOR_OVER);
242
243 TICK_N("Background");
244 widget_draw(WIDGET(state->main_window), d);
245
246#ifdef XCB_IMDKIT
247 if (config.enable_imdkit) {
248 int x = widget_get_x_pos(&state->text->widget) +
250 int y = widget_get_y_pos(&state->text->widget) +
251 widget_get_height(&state->text->widget);
253 }
254#endif
255
256 TICK_N("widgets");
257 cairo_surface_flush(XcbState.edit_surf);
258 if (qr) {
260 }
261}
262
280 int location = rofi_theme_get_position(WIDGET(state->main_window), "location",
281 loc_transtable[config.location]);
282 int anchor =
283 rofi_theme_get_position(WIDGET(state->main_window), "anchor", location);
284
285 if (XcbState.fullscreen) {
286 state->x = XcbState.mon.x;
287 state->y = XcbState.mon.y;
288 return;
289 }
290 state->y = XcbState.mon.y + (XcbState.mon.h) / 2;
291 state->x = XcbState.mon.x + (XcbState.mon.w) / 2;
292 // Determine window location
293 switch (location) {
294 case WL_NORTH_WEST:
295 state->x = XcbState.mon.x;
297 case WL_NORTH:
298 state->y = XcbState.mon.y;
299 break;
300 case WL_NORTH_EAST:
301 state->y = XcbState.mon.y;
303 case WL_EAST:
304 state->x = XcbState.mon.x + XcbState.mon.w;
305 break;
306 case WL_SOUTH_EAST:
307 state->x = XcbState.mon.x + XcbState.mon.w;
309 case WL_SOUTH:
310 state->y = XcbState.mon.y + XcbState.mon.h;
311 break;
312 case WL_SOUTH_WEST:
313 state->y = XcbState.mon.y + XcbState.mon.h;
315 case WL_WEST:
316 state->x = XcbState.mon.x;
317 break;
318 case WL_CENTER:;
320 default:
321 break;
322 }
323 switch (anchor) {
324 case WL_SOUTH_WEST:
325 state->y -= state->height;
326 break;
327 case WL_SOUTH:
328 state->x -= state->width / 2;
329 state->y -= state->height;
330 break;
331 case WL_SOUTH_EAST:
332 state->x -= state->width;
333 state->y -= state->height;
334 break;
335 case WL_NORTH_EAST:
336 state->x -= state->width;
337 break;
338 case WL_NORTH_WEST:
339 break;
340 case WL_NORTH:
341 state->x -= state->width / 2;
342 break;
343 case WL_EAST:
344 state->x -= state->width;
345 state->y -= state->height / 2;
346 break;
347 case WL_WEST:
348 state->y -= state->height / 2;
349 break;
350 case WL_CENTER:
351 state->y -= state->height / 2;
352 state->x -= state->width / 2;
353 break;
354 default:
355 break;
356 }
357 // Apply offset.
359 "x-offset", config.x_offset);
361 "y-offset", config.y_offset);
364}
365
367 if (state == NULL) {
368 return;
369 }
370 uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
371 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
372 uint32_t vals[] = {state->x, state->y, state->width, state->height};
373
374 // Display it.
375 xcb_configure_window(xcb->connection, CacheState.main_window, mask, vals);
376 cairo_destroy(XcbState.edit_draw);
377 cairo_surface_destroy(XcbState.edit_surf);
378
379 xcb_free_pixmap(xcb->connection, XcbState.edit_pixmap);
380 XcbState.edit_pixmap = xcb_generate_id(xcb->connection);
381 xcb_create_pixmap(xcb->connection, depth->depth, XcbState.edit_pixmap,
382 CacheState.main_window, state->width, state->height);
383
384 XcbState.edit_surf =
385 cairo_xcb_surface_create(xcb->connection, XcbState.edit_pixmap, visual,
386 state->width, state->height);
387 XcbState.edit_draw = cairo_create(XcbState.edit_surf);
388
389 g_debug("Re-size window based internal request: %dx%d.", state->width,
390 state->height);
391 // Should wrap main window in a widget.
392 widget_resize(WIDGET(state->main_window), state->width, state->height);
393}
394
396 switch (type) {
398 return CURSOR_DEFAULT;
399
401 return CURSOR_POINTER;
402
403 case ROFI_CURSOR_TEXT:
404 return CURSOR_TEXT;
405 }
406
407 return CURSOR_DEFAULT;
408}
409
412
413 if (x11_type == XcbState.cursor_type) {
414 return;
415 }
416
417 XcbState.cursor_type = x11_type;
418
419 x11_set_cursor(CacheState.main_window, x11_type);
420}
421
423 xcb_query_pointer_cookie_t pointer_cookie =
424 xcb_query_pointer(xcb->connection, CacheState.main_window);
425 xcb_query_pointer_reply_t *pointer_reply =
426 xcb_query_pointer_reply(xcb->connection, pointer_cookie, NULL);
427
428 if (pointer_reply == NULL) {
429 return;
430 }
431
432 rofi_view_handle_mouse_motion(state, pointer_reply->win_x,
433 pointer_reply->win_y, config.hover_select);
434
435 free(pointer_reply);
436}
437
438static gboolean xcb_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data) {
440
441 /* Clear before redrawing: a reload signalled during the pass would
442 * otherwise coalesce into it and be lost. */
443 XcbState.idle_timeout = 0;
444
445 if (state) {
446 // For UI update on this.
447 if (state->tb_total_rows) {
448 char *r = g_strdup_printf("%u", mode_get_num_entries(state->sw));
449 textbox_text(state->tb_total_rows, r);
450 g_free(r);
451 }
452 state->reload = TRUE;
453 state->refilter = TRUE;
455 }
456 return G_SOURCE_REMOVE;
457}
458
459static void xcb_rofi_view_reload(void) {
460 // @TODO add check if current view is equal to the callee
461 if (XcbState.idle_timeout == 0) {
462 XcbState.idle_timeout =
463 g_timeout_add(1000 / 100, xcb_rofi_view_reload_idle, NULL);
464 }
465}
466static void xcb_rofi_view_queue_redraw(void) {
468
469 if (state && XcbState.repaint_source == 0) {
470 XcbState.count++;
471 g_debug("redraw %llu", XcbState.count);
472 XcbState.repaint_source = g_idle_add_full(
473 G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL);
474 }
475}
476
477static void
479 const char *const fake_background) {
480 if (XcbState.fake_bg == NULL) {
481 cairo_surface_t *s = NULL;
486 TICK_N("Fake start");
487 if (g_strcmp0(fake_background, "real") == 0) {
488 return;
489 } else if (g_strcmp0(fake_background, "screenshot") == 0) {
491 } else if (g_strcmp0(fake_background, "background") == 0) {
493 } else {
494 char *fpath = rofi_expand_path(fake_background);
495 g_debug("Opening %s to use as background.", fpath);
496 s = cairo_image_surface_create_from_png(fpath);
497 XcbState.fake_bgrel = TRUE;
498 g_free(fpath);
499 }
500 TICK_N("Get surface.");
501 if (s != NULL) {
502 if (cairo_surface_status(s) != CAIRO_STATUS_SUCCESS) {
503 g_debug("Failed to open surface fake background: %s",
504 cairo_status_to_string(cairo_surface_status(s)));
505 cairo_surface_destroy(s);
506 s = NULL;
507 } else {
508 XcbState.fake_bg = cairo_image_surface_create(
509 CAIRO_FORMAT_ARGB32, XcbState.mon.w, XcbState.mon.h);
510
511 int blur = rofi_theme_get_integer(WIDGET(win), "blur", 0);
512 cairo_t *dr = cairo_create(XcbState.fake_bg);
513 if (XcbState.fake_bgrel) {
514 cairo_set_source_surface(dr, s, 0, 0);
515 } else {
516 cairo_set_source_surface(dr, s, -XcbState.mon.x, -XcbState.mon.y);
517 }
518 cairo_paint(dr);
519 cairo_destroy(dr);
520 cairo_surface_destroy(s);
521 if (blur > 0) {
522 cairo_image_surface_blur(XcbState.fake_bg, (double)blur, 0);
523 TICK_N("BLUR");
524 }
525 }
526 }
527 TICK_N("Fake transparency");
528 }
529}
530
531#ifdef XCB_IMDKIT
532static void xim_commit_string(xcb_xim_t *im, G_GNUC_UNUSED xcb_xic_t ic,
533 G_GNUC_UNUSED uint32_t flag, char *str,
534 uint32_t length, G_GNUC_UNUSED uint32_t *keysym,
535 G_GNUC_UNUSED size_t nKeySym,
536 G_GNUC_UNUSED void *user_data) {
538 if (state == NULL) {
539 return;
540 }
541
542#ifndef XCB_IMDKIT_1_0_3_LOWER
543 if (xcb_xim_get_encoding(im) == XCB_XIM_UTF8_STRING) {
544 rofi_view_handle_text(state, str);
545 } else if (xcb_xim_get_encoding(im) == XCB_XIM_COMPOUND_TEXT) {
546 size_t newLength = 0;
547 char *utf8 = xcb_compound_text_to_utf8(str, length, &newLength);
548 if (utf8) {
549 rofi_view_handle_text(state, utf8);
550 }
551 }
552#else
553 size_t newLength = 0;
554 char *utf8 = xcb_compound_text_to_utf8(str, length, &newLength);
555 if (utf8) {
556 rofi_view_handle_text(state, utf8);
557 }
558#endif
559}
560
561static void xim_disconnected(G_GNUC_UNUSED xcb_xim_t *im,
562 G_GNUC_UNUSED void *user_data) {
563 xcb->ic = 0;
564}
565
566static void create_ic_callback(xcb_xim_t *im, xcb_xic_t new_ic,
567 G_GNUC_UNUSED void *user_data) {
568 xcb->ic = new_ic;
569 if (xcb->ic) {
570 xcb_xim_set_ic_focus(im, xcb->ic);
571 }
572}
573
574gboolean rofi_set_im_window_pos(int new_x, int new_y) {
575 if (!xcb->ic)
576 return false;
577
578 static xcb_point_t spot = {.x = 0, .y = 0};
579 if (spot.x != new_x || spot.y != new_y) {
580 spot.x = new_x;
581 spot.y = new_y;
582 xcb_xim_nested_list nested = xcb_xim_create_nested_list(
583 xcb->im, XCB_XIM_XNSpotLocation, &spot, NULL);
584 xcb_xim_set_ic_values(xcb->im, xcb->ic, NULL, NULL, XCB_XIM_XNClientWindow,
585 &CacheState.main_window, XCB_XIM_XNFocusWindow,
586 &CacheState.main_window, XCB_XIM_XNPreeditAttributes,
587 &nested, NULL);
588 free(nested.data);
589 }
590 return true;
591}
592static void open_xim_callback(xcb_xim_t *im, G_GNUC_UNUSED void *user_data) {
594 uint32_t input_style = XCB_IM_PreeditPosition | XCB_IM_StatusArea;
595 xcb_point_t spot;
596 spot.x = widget_get_x_pos(&state->text->widget) +
598 spot.y = widget_get_y_pos(&state->text->widget) +
599 widget_get_height(&state->text->widget);
600 xcb_xim_nested_list nested =
601 xcb_xim_create_nested_list(im, XCB_XIM_XNSpotLocation, &spot, NULL);
602 xcb_xim_create_ic(
603 im, create_ic_callback, NULL, XCB_XIM_XNInputStyle, &input_style,
604 XCB_XIM_XNClientWindow, &CacheState.main_window, XCB_XIM_XNFocusWindow,
605 &CacheState.main_window, XCB_XIM_XNPreeditAttributes, &nested, NULL);
606 free(nested.data);
607}
608#endif
609
610static void xcb___create_window(MenuFlags menu_flags) {
612
613 uint32_t selmask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL |
614 XCB_CW_BIT_GRAVITY | XCB_CW_BACKING_STORE |
615 XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
616 uint32_t xcb_event_masks =
617 XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS |
618 XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_KEY_PRESS |
619 XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE |
620 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE |
621 XCB_EVENT_MASK_BUTTON_1_MOTION | XCB_EVENT_MASK_POINTER_MOTION;
622
623 uint32_t selval[] = {XCB_BACK_PIXMAP_NONE, 0,
624 XCB_GRAVITY_STATIC, XCB_BACKING_STORE_NOT_USEFUL,
625 xcb_event_masks, map};
626
627#ifdef XCB_IMDKIT
628 if (config.enable_imdkit) {
629 xcb_xim_set_im_callback(xcb->im, &xim_callback, NULL);
630
631 // Open connection to XIM server.
632 xcb_xim_open(xcb->im, open_xim_callback, true, NULL);
633 }
634#endif
635
636 xcb_window_t box_window = xcb_generate_id(xcb->connection);
637 xcb_void_cookie_t cc = xcb_create_window_checked(
638 xcb->connection, depth->depth, box_window, xcb_stuff_get_root_window(), 0,
639 0, 200, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual->visual_id, selmask,
640 selval);
641 xcb_generic_error_t *error;
642 error = xcb_request_check(xcb->connection, cc);
643 if (error) {
644 g_error("xcb_create_window() failed error=0x%x\n", error->error_code);
645 // g_error will cause the program to segfault.
646 // This is an unexpected error.
647 // exit(EXIT_FAILURE);
648 }
649
650 TICK_N("xcb create window");
651 XcbState.gc = xcb_generate_id(xcb->connection);
652 xcb_create_gc(xcb->connection, XcbState.gc, box_window, 0, 0);
653
654 TICK_N("xcb create gc");
655 // Create a drawable.
656 XcbState.edit_pixmap = xcb_generate_id(xcb->connection);
657 xcb_create_pixmap(xcb->connection, depth->depth, XcbState.edit_pixmap,
658 CacheState.main_window, 200, 100);
659
660 XcbState.edit_surf = cairo_xcb_surface_create(
661 xcb->connection, XcbState.edit_pixmap, visual, 200, 100);
662 XcbState.edit_draw = cairo_create(XcbState.edit_surf);
663
664 TICK_N("create cairo surface");
665 // Set up pango context.
666 cairo_font_options_t *fo = cairo_font_options_create();
667 // Take font description from xlib surface
668 cairo_surface_get_font_options(XcbState.edit_surf, fo);
669 // TODO should we update the drawable each time?
670 PangoContext *p = pango_cairo_create_context(XcbState.edit_draw);
671 // Set the font options from the xlib surface
672 pango_cairo_context_set_font_options(p, fo);
673 TICK_N("pango cairo font setup");
674
675 CacheState.main_window = box_window;
676 CacheState.flags = menu_flags;
677 monitor_active(&(XcbState.mon));
678 // Setup dpi
679 if (config.dpi > 1) {
680 PangoFontMap *font_map = pango_cairo_font_map_get_default();
681 pango_cairo_font_map_set_resolution((PangoCairoFontMap *)font_map,
682 (double)config.dpi);
683 } else if (config.dpi == 0 || config.dpi == 1) {
684 // Auto-detect mode.
685 double dpi = 96;
686 if (XcbState.mon.mh > 0 && config.dpi == 1) {
687 dpi = (XcbState.mon.h * 25.4) / (double)(XcbState.mon.mh);
688 } else {
689 dpi = (xcb->screen->height_in_pixels * 25.4) /
690 (double)(xcb->screen->height_in_millimeters);
691 }
692
693 g_debug("Auto-detected DPI: %.2lf", dpi);
694 PangoFontMap *font_map = pango_cairo_font_map_get_default();
695 pango_cairo_font_map_set_resolution((PangoCairoFontMap *)font_map, dpi);
696 config.dpi = dpi;
697 } else {
698 // default pango is 96.
699 PangoFontMap *font_map = pango_cairo_font_map_get_default();
700 config.dpi =
701 pango_cairo_font_map_get_resolution((PangoCairoFontMap *)font_map);
702 }
703 // Setup font.
704 // Dummy widget.
705 box *win = box_create(NULL, "window", ROFI_ORIENTATION_HORIZONTAL);
706 const char *font =
707 rofi_theme_get_string(WIDGET(win), "font", config.menu_font);
708 if (font) {
709 PangoFontDescription *pfd = pango_font_description_from_string(font);
710 if (helper_validate_font(pfd, font)) {
711 pango_context_set_font_description(p, pfd);
712 }
713 pango_font_description_free(pfd);
714 }
715 PangoLanguage *l = pango_language_get_default();
716 pango_context_set_language(p, l);
717 TICK_N("configure font");
718
719 // Tell textbox to use this context.
721 // cleanup
722 g_object_unref(p);
723 cairo_font_options_destroy(fo);
724
725 TICK_N("textbox setup");
726 // // make it an unmanaged window
727 if (((menu_flags & MENU_TRANSIENT_WINDOW) != 0)) {
728 xcb_atom_t atoms[] = {xcb->ewmh._NET_WM_STATE_MODAL};
729
730 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_STATE, atoms,
731 sizeof(atoms) / sizeof(xcb_atom_t));
732 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_WINDOW_TYPE,
733 &(xcb->ewmh._NET_WM_WINDOW_TYPE_UTILITY), 1);
734 x11_disable_decoration(box_window);
735
736 xcb_window_t active_window;
737 xcb_get_property_cookie_t awc;
738 awc = xcb_ewmh_get_active_window(&xcb->ewmh, xcb->screen_nbr);
739
740 if (xcb_ewmh_get_active_window_reply(&xcb->ewmh, awc, &active_window,
741 NULL)) {
742 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE, box_window,
743 XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, 1,
744 &active_window);
745 }
746 } else if (((menu_flags & MENU_NORMAL_WINDOW) == 0)) {
747 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_STATE,
748 &(xcb->ewmh._NET_WM_STATE_ABOVE), 1);
749 uint32_t values[] = {1};
750 xcb_change_window_attributes(xcb->connection, box_window,
751 XCB_CW_OVERRIDE_REDIRECT, values);
752 } else {
753 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_WINDOW_TYPE,
754 &(xcb->ewmh._NET_WM_WINDOW_TYPE_NORMAL), 1);
755 x11_disable_decoration(box_window);
756 }
757
758 TICK_N("setup window attributes");
759 XcbState.fullscreen =
760 rofi_theme_get_boolean(WIDGET(win), "fullscreen", FALSE);
761 if (XcbState.fullscreen) {
762 xcb_atom_t atoms[] = {xcb->ewmh._NET_WM_STATE_FULLSCREEN,
763 xcb->ewmh._NET_WM_STATE_ABOVE};
764 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_STATE, atoms,
765 sizeof(atoms) / sizeof(xcb_atom_t));
766 }
767
768 xcb_atom_t protocols[] = {netatoms[WM_TAKE_FOCUS]};
769 xcb_icccm_set_wm_protocols(xcb->connection, box_window,
770 xcb->ewmh.WM_PROTOCOLS, G_N_ELEMENTS(protocols),
771 protocols);
772
773 TICK_N("setup window fullscreen");
774 // Set the WM_NAME
776 const char wm_class_name[] = "rofi\0Rofi";
777 xcb_icccm_set_wm_class(xcb->connection, box_window, sizeof(wm_class_name),
778 wm_class_name);
779
780 TICK_N("setup window name and class");
781 const char *transparency =
782 rofi_theme_get_string(WIDGET(win), "transparency", NULL);
783 if (transparency) {
785 }
786 if (xcb->sncontext != NULL) {
787 sn_launchee_context_setup_window(xcb->sncontext, CacheState.main_window);
788 }
789 TICK_N("setup startup notification");
790 widget_free(WIDGET(win));
791 TICK_N("done");
792
793 // Set the PID.
794 pid_t pid = getpid();
795 xcb_ewmh_set_wm_pid(&(xcb->ewmh), CacheState.main_window, pid);
796
797 // Get hostname
798 const char *hostname = g_get_host_name();
799 char *ahost = g_hostname_to_ascii(hostname);
800 if (ahost != NULL) {
801 xcb_icccm_set_wm_client_machine(xcb->connection, CacheState.main_window,
802 XCB_ATOM_STRING, 8, strlen(ahost), ahost);
803 g_free(ahost);
804 }
805}
806
813 if (XcbState.fullscreen) {
814 state->width = XcbState.mon.w;
815 return;
816 }
817 // Calculate as float to stop silly, big rounding down errors.
818 state->width = (XcbState.mon.w / 100.0f) * DEFAULT_MENU_WIDTH;
819 // Use theme configured width, if set.
821 "width", state->width);
823}
824
829static void
832 if (xce->window == CacheState.main_window) {
833 if (state->x != xce->x || state->y != xce->y) {
834 state->x = xce->x;
835 state->y = xce->y;
837 }
838 if (state->width != xce->width || state->height != xce->height) {
839 state->width = xce->width;
840 state->height = xce->height;
841
842 cairo_destroy(XcbState.edit_draw);
843 cairo_surface_destroy(XcbState.edit_surf);
844
845 xcb_free_pixmap(xcb->connection, XcbState.edit_pixmap);
846 XcbState.edit_pixmap = xcb_generate_id(xcb->connection);
847 xcb_create_pixmap(xcb->connection, depth->depth, XcbState.edit_pixmap,
848 CacheState.main_window, state->width, state->height);
849
850 XcbState.edit_surf =
851 cairo_xcb_surface_create(xcb->connection, XcbState.edit_pixmap,
852 visual, state->width, state->height);
853 XcbState.edit_draw = cairo_create(XcbState.edit_surf);
854 g_debug("Re-size window based external request: %d %d", state->width,
855 state->height);
856 widget_resize(WIDGET(state->main_window), state->width, state->height);
857 }
858 }
859}
860
865 xcb_window_t target) {
866 if ((CacheState.flags & MENU_NORMAL_WINDOW) == 0) {
867 if (target != CacheState.main_window) {
868 state->quit = TRUE;
869 state->retv = MENU_CANCEL;
870 }
871 }
872}
873
875 if (XcbState.repaint_source == 0) {
876 XcbState.count++;
877 g_debug("redraw %llu", XcbState.count);
878 XcbState.repaint_source = g_idle_add_full(
879 G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL);
880 }
881}
882
884 if (XcbState.fullscreen == TRUE) {
885 return XcbState.mon.h;
886 }
887
888 RofiDistance h =
889 rofi_theme_get_distance(WIDGET(state->main_window), "height", 0);
890 unsigned int height = distance_get_pixel(h, ROFI_ORIENTATION_VERTICAL);
891 // If height is set, return it.
892 if (height > 0) {
893 return height;
894 }
895 // Autosize based on widgets.
896 widget *main_window = WIDGET(state->main_window);
897 return widget_get_desired_height(main_window, state->width);
898}
899
900static void xcb_rofi_view_hide(void) {
901 if (CacheState.main_window != XCB_WINDOW_NONE) {
903 xcb_unmap_window(xcb->connection, CacheState.main_window);
905 }
906}
907
908static void xcb_rofi_view_cleanup(void) {
909 // Clear clipboard data.
911 g_debug("Cleanup.");
912 if (XcbState.idle_timeout > 0) {
913 g_source_remove(XcbState.idle_timeout);
914 XcbState.idle_timeout = 0;
915 }
916 if (CacheState.refilter_timeout > 0) {
917 g_source_remove(CacheState.refilter_timeout);
918 CacheState.refilter_timeout = 0;
919 }
920 if (CacheState.overlay_timeout) {
921 g_source_remove(CacheState.overlay_timeout);
922 CacheState.overlay_timeout = 0;
923 }
924 if (CacheState.user_timeout > 0) {
925 g_source_remove(CacheState.user_timeout);
926 CacheState.user_timeout = 0;
927 }
928 if (XcbState.repaint_source > 0) {
929 g_source_remove(XcbState.repaint_source);
930 XcbState.repaint_source = 0;
931 }
932 if (XcbState.fake_bg) {
933 cairo_surface_destroy(XcbState.fake_bg);
934 XcbState.fake_bg = NULL;
935 }
936 if (XcbState.edit_draw) {
937 cairo_destroy(XcbState.edit_draw);
938 XcbState.edit_draw = NULL;
939 }
940 if (XcbState.edit_surf) {
941 cairo_surface_destroy(XcbState.edit_surf);
942 XcbState.edit_surf = NULL;
943 }
944 if (CacheState.main_window != XCB_WINDOW_NONE) {
945 g_debug("Unmapping and free'ing window");
946 xcb_unmap_window(xcb->connection, CacheState.main_window);
947 xcb_free_gc(xcb->connection, XcbState.gc);
948 xcb_free_pixmap(xcb->connection, XcbState.edit_pixmap);
949 xcb_destroy_window(xcb->connection, CacheState.main_window);
950 CacheState.main_window = XCB_WINDOW_NONE;
951 }
952 if (map != XCB_COLORMAP_NONE) {
953 xcb_free_colormap(xcb->connection, map);
954 map = XCB_COLORMAP_NONE;
955 }
956 xcb_flush(xcb->connection);
957 g_assert(g_queue_is_empty(&(CacheState.views)));
958
960}
961
963 return CacheState.main_window;
964}
965
966static void xcb_rofi_view_set_window_title(const char *title) {
967 ssize_t len = strlen(title);
968 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
969 CacheState.main_window, xcb->ewmh._NET_WM_NAME,
970 xcb->ewmh.UTF8_STRING, 8, len, title);
971 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
972 CacheState.main_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING,
973 8, len, title);
974}
975
977 .update = xcb_rofi_view_update,
978 .temp_configure_notify = xcb_rofi_view_temp_configure_notify,
979 .temp_click_to_exit = xcb_rofi_view_temp_click_to_exit,
980 .frame_callback = xcb_rofi_view_frame_callback,
981 .queue_redraw = xcb_rofi_view_queue_redraw,
982
983 .set_window_title = xcb_rofi_view_set_window_title,
984 .calculate_window_position = xcb_rofi_view_calculate_window_position,
985 .calculate_window_width = xcb_rofi_view_calculate_window_width,
986 .calculate_window_height = xcb_rofi_view_calculate_window_height,
987 .window_update_size = xcb_rofi_view_window_update_size,
988 .set_cursor = xcb_rofi_view_set_cursor,
989 .ping_mouse = xcb_rofi_view_ping_mouse,
990
991 .cleanup = xcb_rofi_view_cleanup,
992 .hide = xcb_rofi_view_hide,
993 .reload = xcb_rofi_view_reload,
994
995 .__create_window = xcb___create_window,
996 .get_window = xcb_rofi_view_get_window,
997 .get_current_monitor = xcb_rofi_view_get_current_monitor,
998
999 .set_size = NULL,
1000 .get_size = NULL,
1001};
1002
int monitor_active(workarea *mon)
Definition display.c:19
void display_early_cleanup(void)
Definition display.c:52
void display_revert_input_focus(void)
Definition display.c:32
struct _workarea workarea
@ WL_SOUTH_EAST
Definition rofi-types.h:249
@ WL_CENTER
Definition rofi-types.h:235
@ WL_NORTH_WEST
Definition rofi-types.h:245
@ WL_SOUTH
Definition rofi-types.h:241
@ WL_NORTH_EAST
Definition rofi-types.h:247
@ WL_WEST
Definition rofi-types.h:243
@ WL_NORTH
Definition rofi-types.h:237
@ WL_EAST
Definition rofi-types.h:239
@ WL_SOUTH_WEST
Definition rofi-types.h:251
gboolean helper_validate_font(PangoFontDescription *pfd, const char *font)
Definition helper.c:652
#define rofi_fallthrough
Definition helper.h:540
char * rofi_expand_path(const char *input)
Definition helper.c:784
unsigned int mode_get_num_entries(const Mode *mode)
Definition mode.c:70
@ MENU_CANCEL
Definition mode.h:74
#define TICK()
Definition timings.h:64
#define TICK_N(a)
Definition timings.h:69
void textbox_set_pango_context(const char *font, PangoContext *p)
Definition textbox.c:1034
void textbox_text(textbox *tb, const char *text)
Definition textbox.c:412
int textbox_get_cursor_x_pos(const textbox *tb)
Definition textbox.c:1160
void rofi_view_queue_redraw(void)
Definition view.c:2193
RofiViewState * rofi_view_get_active(void)
Definition view.c:321
MenuFlags
Definition view.h:55
void rofi_view_handle_text(RofiViewState *state, const char *text)
Definition view.c:1463
void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y, gboolean find_mouse_target)
Definition view.c:1500
@ MENU_TRANSIENT_WINDOW
Definition view.h:65
@ MENU_NORMAL_WINDOW
Definition view.h:61
gboolean rofi_set_im_window_pos(int new_x, int new_y)
const view_proxy * xcb_view_proxy
Definition view.c:1003
void input_history_save(void)
Definition view.c:580
void input_history_initialize(void)
Definition view.c:537
box * box_create(widget *parent, const char *name, RofiOrientation type)
Definition box.c:347
struct _box box
Definition box.h:49
void widget_queue_redraw(widget *wid)
Definition widget.c:487
void widget_free(widget *wid)
Definition widget.c:425
void widget_draw(widget *wid, cairo_t *d)
Definition widget.c:140
int widget_get_height(widget *wid)
Definition widget.c:437
struct _widget widget
Definition widget.h:49
int widget_get_y_pos(widget *wid)
Definition widget.c:461
int widget_get_x_pos(widget *wid)
Definition widget.c:455
void widget_resize(widget *wid, short w, short h)
Definition widget.c:92
#define WIDGET(a)
Definition widget.h:117
int widget_get_desired_height(widget *wid, const int width)
Definition widget.c:644
gboolean widget_need_redraw(widget *wid)
Definition widget.c:500
@ ROFI_ORIENTATION_HORIZONTAL
Definition rofi-types.h:141
@ ROFI_ORIENTATION_VERTICAL
Definition rofi-types.h:140
RofiCursorType
Definition rofi-types.h:147
@ ROFI_CURSOR_POINTER
Definition rofi-types.h:149
@ ROFI_CURSOR_TEXT
Definition rofi-types.h:150
@ ROFI_CURSOR_DEFAULT
Definition rofi-types.h:148
Settings config
#define DEFAULT_MENU_WIDTH
Definition settings.h:235
textbox * text
MenuReturn retv
textbox * tb_total_rows
widget widget
Definition textbox.h:61
int rofi_theme_get_integer(const widget *wid, const char *property, int def)
Definition theme.c:842
int rofi_theme_get_position(const widget *wid, const char *property, int def)
Definition theme.c:818
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
Definition theme.c:1404
int rofi_theme_get_boolean(const widget *wid, const char *property, int def)
Definition theme.c:903
RofiDistance rofi_theme_get_distance(const widget *wid, const char *property, int def)
Definition theme.c:877
const char * rofi_theme_get_string(const widget *wid, const char *property, const char *def)
Definition theme.c:989
struct _view_proxy view_proxy
void rofi_view_update(RofiViewState *state, gboolean qr)
Definition view.c:2178
GThreadPool * tpool
Definition view.c:70
struct _rofi_view_cache_state CacheState
Definition view.c:75
guint idle_timeout
Definition view.c:75
static const int loc_transtable[9]
Definition view.c:141
unsigned long long count
Definition view.c:77
static view_proxy view_
Definition view.c:553
gboolean fullscreen
Definition view.c:81
guint repaint_source
Definition view.c:79
int xcb_window_t
Definition xcb-dummy.h:9
#define XCB_WINDOW_NONE
Definition xcb-dummy.h:12
int xcb_configure_notify_event_t
Definition xcb-dummy.h:8
xcb_colormap_t map
Definition display.c:110
cairo_surface_t * x11_helper_get_screenshot_surface(void)
Definition display.c:357
xcb_stuff * xcb
Definition display.c:103
xcb_depth_t * depth
Definition display.c:108
cairo_surface_t * x11_helper_get_bg_surface(void)
Definition display.c:383
void x11_set_cursor(xcb_window_t window, X11CursorType type)
Definition display.c:1989
void cairo_image_surface_blur(cairo_surface_t *surface, int radius, double deviation)
Definition display.c:178
xcb_window_t xcb_stuff_get_root_window(void)
Definition display.c:1923
void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms, int count)
Definition display.c:423
void xcb_stuff_set_clipboard(char *data)
Definition display.c:2001
void x11_disable_decoration(xcb_window_t window)
Definition display.c:1965
xcb_atom_t netatoms[NUM_NETATOMS]
Definition display.c:115
xcb_visualtype_t * visual
Definition display.c:109
cairo_surface_t * fake_bg
Definition view.c:105
static struct @155221133275114015303222275063026125104100003333 BenchMark
static void xcb_rofi_view_get_current_monitor(int *width, int *height)
Definition view.c:139
static gboolean bench_update(void)
Definition view.c:168
static void xcb_rofi_view_temp_configure_notify(RofiViewState *state, xcb_configure_notify_event_t *xce)
Definition view.c:830
static void xcb_rofi_view_calculate_window_width(RofiViewState *state)
Definition view.c:812
static void xcb___create_window(MenuFlags menu_flags)
Definition view.c:610
GTimer * time
Definition view.c:159
static void xcb_rofi_view_window_update_size(RofiViewState *state)
Definition view.c:366
static void xcb_rofi_view_set_window_title(const char *title)
Definition view.c:966
static struct @245150067040235115245101050276046267042376347112 XcbState
cairo_surface_t * edit_surf
Definition view.c:111
static void xcb_rofi_view_update(RofiViewState *state, gboolean qr)
Definition view.c:218
static void xcb_rofi_view_setup_fake_transparency(widget *win, const char *const fake_background)
Definition view.c:478
static void xcb_rofi_view_cleanup(void)
Definition view.c:908
gboolean do_bench
Definition view.c:152
static void xcb_rofi_view_set_cursor(RofiCursorType type)
Definition view.c:410
xcb_gcontext_t gc
Definition view.c:107
static gboolean xcb_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data)
Definition view.c:438
cairo_t * edit_draw
Definition view.c:113
static gboolean xcb_rofi_view_repaint(G_GNUC_UNUSED void *data)
Definition view.c:192
static void xcb_rofi_view_calculate_window_position(RofiViewState *state)
Definition view.c:279
xcb_pixmap_t edit_pixmap
Definition view.c:109
workarea mon
Definition view.c:117
static int xcb_rofi_view_calculate_window_height(RofiViewState *state)
Definition view.c:883
int fake_bgrel
Definition view.c:115
static void xcb_rofi_view_reload(void)
Definition view.c:459
static void xcb_rofi_view_frame_callback(void)
Definition view.c:874
X11CursorType cursor_type
Definition view.c:127
static xcb_window_t xcb_rofi_view_get_window(void)
Definition view.c:962
double min
Definition view.c:165
static void xcb_rofi_view_temp_click_to_exit(RofiViewState *state, xcb_window_t target)
Definition view.c:864
uint64_t draws
Definition view.c:161
double last_ts
Definition view.c:163
static void xcb_rofi_view_ping_mouse(RofiViewState *state)
Definition view.c:422
static void xcb_rofi_view_queue_redraw(void)
Definition view.c:466
static X11CursorType rofi_cursor_type_to_x11_cursor_type(RofiCursorType type)
Definition view.c:395
static void xcb_rofi_view_hide(void)
Definition view.c:900
X11CursorType
Definition xcb.h:140
@ CURSOR_POINTER
Definition xcb.h:144
@ CURSOR_DEFAULT
Definition xcb.h:142
@ CURSOR_TEXT
Definition xcb.h:146