rofi 2.0.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
263
281 int location = rofi_theme_get_position(WIDGET(state->main_window), "location",
282 loc_transtable[config.location]);
283 int anchor =
284 rofi_theme_get_position(WIDGET(state->main_window), "anchor", location);
285
286 if (XcbState.fullscreen) {
287 state->x = XcbState.mon.x;
288 state->y = XcbState.mon.y;
289 return;
290 }
291 state->y = XcbState.mon.y + (XcbState.mon.h) / 2;
292 state->x = XcbState.mon.x + (XcbState.mon.w) / 2;
293 // Determine window location
294 switch (location) {
295 case WL_NORTH_WEST:
296 state->x = XcbState.mon.x;
298 case WL_NORTH:
299 state->y = XcbState.mon.y;
300 break;
301 case WL_NORTH_EAST:
302 state->y = XcbState.mon.y;
304 case WL_EAST:
305 state->x = XcbState.mon.x + XcbState.mon.w;
306 break;
307 case WL_SOUTH_EAST:
308 state->x = XcbState.mon.x + XcbState.mon.w;
310 case WL_SOUTH:
311 state->y = XcbState.mon.y + XcbState.mon.h;
312 break;
313 case WL_SOUTH_WEST:
314 state->y = XcbState.mon.y + XcbState.mon.h;
316 case WL_WEST:
317 state->x = XcbState.mon.x;
318 break;
319 case WL_CENTER:;
321 default:
322 break;
323 }
324 switch (anchor) {
325 case WL_SOUTH_WEST:
326 state->y -= state->height;
327 break;
328 case WL_SOUTH:
329 state->x -= state->width / 2;
330 state->y -= state->height;
331 break;
332 case WL_SOUTH_EAST:
333 state->x -= state->width;
334 state->y -= state->height;
335 break;
336 case WL_NORTH_EAST:
337 state->x -= state->width;
338 break;
339 case WL_NORTH_WEST:
340 break;
341 case WL_NORTH:
342 state->x -= state->width / 2;
343 break;
344 case WL_EAST:
345 state->x -= state->width;
346 state->y -= state->height / 2;
347 break;
348 case WL_WEST:
349 state->y -= state->height / 2;
350 break;
351 case WL_CENTER:
352 state->y -= state->height / 2;
353 state->x -= state->width / 2;
354 break;
355 default:
356 break;
357 }
358 // Apply offset.
360 "x-offset", config.x_offset);
362 "y-offset", config.y_offset);
365}
366
368 if (state == NULL) {
369 return;
370 }
371 uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
372 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
373 uint32_t vals[] = {state->x, state->y, state->width, state->height};
374
375 // Display it.
376 xcb_configure_window(xcb->connection, CacheState.main_window, mask, vals);
377 cairo_destroy(XcbState.edit_draw);
378 cairo_surface_destroy(XcbState.edit_surf);
379
380 xcb_free_pixmap(xcb->connection, XcbState.edit_pixmap);
381 XcbState.edit_pixmap = xcb_generate_id(xcb->connection);
382 xcb_create_pixmap(xcb->connection, depth->depth, XcbState.edit_pixmap,
383 CacheState.main_window, state->width, state->height);
384
385 XcbState.edit_surf =
386 cairo_xcb_surface_create(xcb->connection, XcbState.edit_pixmap, visual,
387 state->width, state->height);
388 XcbState.edit_draw = cairo_create(XcbState.edit_surf);
389
390 g_debug("Re-size window based internal request: %dx%d.", state->width,
391 state->height);
392 // Should wrap main window in a widget.
393 widget_resize(WIDGET(state->main_window), state->width, state->height);
394}
395
397 switch (type) {
399 return CURSOR_DEFAULT;
400
402 return CURSOR_POINTER;
403
404 case ROFI_CURSOR_TEXT:
405 return CURSOR_TEXT;
406 }
407
408 return CURSOR_DEFAULT;
409}
410
413
414 if (x11_type == XcbState.cursor_type) {
415 return;
416 }
417
418 XcbState.cursor_type = x11_type;
419
420 x11_set_cursor(CacheState.main_window, x11_type);
421}
422
424 xcb_query_pointer_cookie_t pointer_cookie =
425 xcb_query_pointer(xcb->connection, CacheState.main_window);
426 xcb_query_pointer_reply_t *pointer_reply =
427 xcb_query_pointer_reply(xcb->connection, pointer_cookie, NULL);
428
429 if (pointer_reply == NULL) {
430 return;
431 }
432
433 rofi_view_handle_mouse_motion(state, pointer_reply->win_x,
434 pointer_reply->win_y, config.hover_select);
435
436 free(pointer_reply);
437}
438
439static gboolean xcb_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data) {
441
442 if (state) {
443 // For UI update on this.
444 if (state->tb_total_rows) {
445 char *r = g_strdup_printf("%u", mode_get_num_entries(state->sw));
446 textbox_text(state->tb_total_rows, r);
447 g_free(r);
448 }
449 state->reload = TRUE;
450 state->refilter = TRUE;
452 }
453 XcbState.idle_timeout = 0;
454 return G_SOURCE_REMOVE;
455}
456
457static void xcb_rofi_view_reload(void) {
458 // @TODO add check if current view is equal to the callee
459 if (XcbState.idle_timeout == 0) {
460 XcbState.idle_timeout =
461 g_timeout_add(1000 / 100, xcb_rofi_view_reload_idle, NULL);
462 }
463}
464static void xcb_rofi_view_queue_redraw(void) {
466
467 if (state && XcbState.repaint_source == 0) {
468 XcbState.count++;
469 g_debug("redraw %llu", XcbState.count);
470 XcbState.repaint_source = g_idle_add_full(
471 G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL);
472 }
473}
474
475static void
477 const char *const fake_background) {
478 if (XcbState.fake_bg == NULL) {
479 cairo_surface_t *s = NULL;
484 TICK_N("Fake start");
485 if (g_strcmp0(fake_background, "real") == 0) {
486 return;
487 } else if (g_strcmp0(fake_background, "screenshot") == 0) {
489 } else if (g_strcmp0(fake_background, "background") == 0) {
491 } else {
492 char *fpath = rofi_expand_path(fake_background);
493 g_debug("Opening %s to use as background.", fpath);
494 s = cairo_image_surface_create_from_png(fpath);
495 XcbState.fake_bgrel = TRUE;
496 g_free(fpath);
497 }
498 TICK_N("Get surface.");
499 if (s != NULL) {
500 if (cairo_surface_status(s) != CAIRO_STATUS_SUCCESS) {
501 g_debug("Failed to open surface fake background: %s",
502 cairo_status_to_string(cairo_surface_status(s)));
503 cairo_surface_destroy(s);
504 s = NULL;
505 } else {
506 XcbState.fake_bg = cairo_image_surface_create(
507 CAIRO_FORMAT_ARGB32, XcbState.mon.w, XcbState.mon.h);
508
509 int blur = rofi_theme_get_integer(WIDGET(win), "blur", 0);
510 cairo_t *dr = cairo_create(XcbState.fake_bg);
511 if (XcbState.fake_bgrel) {
512 cairo_set_source_surface(dr, s, 0, 0);
513 } else {
514 cairo_set_source_surface(dr, s, -XcbState.mon.x, -XcbState.mon.y);
515 }
516 cairo_paint(dr);
517 cairo_destroy(dr);
518 cairo_surface_destroy(s);
519 if (blur > 0) {
520 cairo_image_surface_blur(XcbState.fake_bg, (double)blur, 0);
521 TICK_N("BLUR");
522 }
523 }
524 }
525 TICK_N("Fake transparency");
526 }
527}
528
529#ifdef XCB_IMDKIT
530static void xim_commit_string(xcb_xim_t *im, G_GNUC_UNUSED xcb_xic_t ic,
531 G_GNUC_UNUSED uint32_t flag, char *str,
532 uint32_t length, G_GNUC_UNUSED uint32_t *keysym,
533 G_GNUC_UNUSED size_t nKeySym,
534 G_GNUC_UNUSED void *user_data) {
536 if (state == NULL) {
537 return;
538 }
539
540#ifndef XCB_IMDKIT_1_0_3_LOWER
541 if (xcb_xim_get_encoding(im) == XCB_XIM_UTF8_STRING) {
542 rofi_view_handle_text(state, str);
543 } else if (xcb_xim_get_encoding(im) == XCB_XIM_COMPOUND_TEXT) {
544 size_t newLength = 0;
545 char *utf8 = xcb_compound_text_to_utf8(str, length, &newLength);
546 if (utf8) {
547 rofi_view_handle_text(state, utf8);
548 }
549 }
550#else
551 size_t newLength = 0;
552 char *utf8 = xcb_compound_text_to_utf8(str, length, &newLength);
553 if (utf8) {
554 rofi_view_handle_text(state, utf8);
555 }
556#endif
557}
558
559static void xim_disconnected(G_GNUC_UNUSED xcb_xim_t *im,
560 G_GNUC_UNUSED void *user_data) {
561 xcb->ic = 0;
562}
563
564static void create_ic_callback(xcb_xim_t *im, xcb_xic_t new_ic,
565 G_GNUC_UNUSED void *user_data) {
566 xcb->ic = new_ic;
567 if (xcb->ic) {
568 xcb_xim_set_ic_focus(im, xcb->ic);
569 }
570}
571
572gboolean rofi_set_im_window_pos(int new_x, int new_y) {
573 if (!xcb->ic)
574 return false;
575
576 static xcb_point_t spot = {.x = 0, .y = 0};
577 if (spot.x != new_x || spot.y != new_y) {
578 spot.x = new_x;
579 spot.y = new_y;
580 xcb_xim_nested_list nested = xcb_xim_create_nested_list(
581 xcb->im, XCB_XIM_XNSpotLocation, &spot, NULL);
582 xcb_xim_set_ic_values(xcb->im, xcb->ic, NULL, NULL, XCB_XIM_XNClientWindow,
583 &CacheState.main_window, XCB_XIM_XNFocusWindow,
584 &CacheState.main_window, XCB_XIM_XNPreeditAttributes,
585 &nested, NULL);
586 free(nested.data);
587 }
588 return true;
589}
590static void open_xim_callback(xcb_xim_t *im, G_GNUC_UNUSED void *user_data) {
592 uint32_t input_style = XCB_IM_PreeditPosition | XCB_IM_StatusArea;
593 xcb_point_t spot;
594 spot.x = widget_get_x_pos(&state->text->widget) +
596 spot.y = widget_get_y_pos(&state->text->widget) +
597 widget_get_height(&state->text->widget);
598 xcb_xim_nested_list nested =
599 xcb_xim_create_nested_list(im, XCB_XIM_XNSpotLocation, &spot, NULL);
600 xcb_xim_create_ic(
601 im, create_ic_callback, NULL, XCB_XIM_XNInputStyle, &input_style,
602 XCB_XIM_XNClientWindow, &CacheState.main_window, XCB_XIM_XNFocusWindow,
603 &CacheState.main_window, XCB_XIM_XNPreeditAttributes, &nested, NULL);
604 free(nested.data);
605}
606#endif
607
608static void xcb___create_window(MenuFlags menu_flags) {
610
611 uint32_t selmask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL |
612 XCB_CW_BIT_GRAVITY | XCB_CW_BACKING_STORE |
613 XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
614 uint32_t xcb_event_masks =
615 XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS |
616 XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_KEY_PRESS |
617 XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE |
618 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE |
619 XCB_EVENT_MASK_BUTTON_1_MOTION | XCB_EVENT_MASK_POINTER_MOTION;
620
621 uint32_t selval[] = {XCB_BACK_PIXMAP_NONE, 0,
622 XCB_GRAVITY_STATIC, XCB_BACKING_STORE_NOT_USEFUL,
623 xcb_event_masks, map};
624
625#ifdef XCB_IMDKIT
626 if (config.enable_imdkit) {
627 xcb_xim_set_im_callback(xcb->im, &xim_callback, NULL);
628
629 // Open connection to XIM server.
630 xcb_xim_open(xcb->im, open_xim_callback, true, NULL);
631 }
632#endif
633
634 xcb_window_t box_window = xcb_generate_id(xcb->connection);
635 xcb_void_cookie_t cc = xcb_create_window_checked(
636 xcb->connection, depth->depth, box_window, xcb_stuff_get_root_window(), 0,
637 0, 200, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual->visual_id, selmask,
638 selval);
639 xcb_generic_error_t *error;
640 error = xcb_request_check(xcb->connection, cc);
641 if (error) {
642 g_error("xcb_create_window() failed error=0x%x\n", error->error_code);
643 exit(EXIT_FAILURE);
644 }
645
646 TICK_N("xcb create window");
647 XcbState.gc = xcb_generate_id(xcb->connection);
648 xcb_create_gc(xcb->connection, XcbState.gc, box_window, 0, 0);
649
650 TICK_N("xcb create gc");
651 // Create a drawable.
652 XcbState.edit_pixmap = xcb_generate_id(xcb->connection);
653 xcb_create_pixmap(xcb->connection, depth->depth, XcbState.edit_pixmap,
654 CacheState.main_window, 200, 100);
655
656 XcbState.edit_surf = cairo_xcb_surface_create(
657 xcb->connection, XcbState.edit_pixmap, visual, 200, 100);
658 XcbState.edit_draw = cairo_create(XcbState.edit_surf);
659
660 TICK_N("create cairo surface");
661 // Set up pango context.
662 cairo_font_options_t *fo = cairo_font_options_create();
663 // Take font description from xlib surface
664 cairo_surface_get_font_options(XcbState.edit_surf, fo);
665 // TODO should we update the drawable each time?
666 PangoContext *p = pango_cairo_create_context(XcbState.edit_draw);
667 // Set the font options from the xlib surface
668 pango_cairo_context_set_font_options(p, fo);
669 TICK_N("pango cairo font setup");
670
671 CacheState.main_window = box_window;
672 CacheState.flags = menu_flags;
673 monitor_active(&(XcbState.mon));
674 // Setup dpi
675 if (config.dpi > 1) {
676 PangoFontMap *font_map = pango_cairo_font_map_get_default();
677 pango_cairo_font_map_set_resolution((PangoCairoFontMap *)font_map,
678 (double)config.dpi);
679 } else if (config.dpi == 0 || config.dpi == 1) {
680 // Auto-detect mode.
681 double dpi = 96;
682 if (XcbState.mon.mh > 0 && config.dpi == 1) {
683 dpi = (XcbState.mon.h * 25.4) / (double)(XcbState.mon.mh);
684 } else {
685 dpi = (xcb->screen->height_in_pixels * 25.4) /
686 (double)(xcb->screen->height_in_millimeters);
687 }
688
689 g_debug("Auto-detected DPI: %.2lf", dpi);
690 PangoFontMap *font_map = pango_cairo_font_map_get_default();
691 pango_cairo_font_map_set_resolution((PangoCairoFontMap *)font_map, dpi);
692 config.dpi = dpi;
693 } else {
694 // default pango is 96.
695 PangoFontMap *font_map = pango_cairo_font_map_get_default();
696 config.dpi =
697 pango_cairo_font_map_get_resolution((PangoCairoFontMap *)font_map);
698 }
699 // Setup font.
700 // Dummy widget.
701 box *win = box_create(NULL, "window", ROFI_ORIENTATION_HORIZONTAL);
702 const char *font =
703 rofi_theme_get_string(WIDGET(win), "font", config.menu_font);
704 if (font) {
705 PangoFontDescription *pfd = pango_font_description_from_string(font);
706 if (helper_validate_font(pfd, font)) {
707 pango_context_set_font_description(p, pfd);
708 }
709 pango_font_description_free(pfd);
710 }
711 PangoLanguage *l = pango_language_get_default();
712 pango_context_set_language(p, l);
713 TICK_N("configure font");
714
715 // Tell textbox to use this context.
717 // cleanup
718 g_object_unref(p);
719 cairo_font_options_destroy(fo);
720
721 TICK_N("textbox setup");
722 // // make it an unmanaged window
723 if (((menu_flags & MENU_NORMAL_WINDOW) == 0)) {
724 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_STATE,
725 &(xcb->ewmh._NET_WM_STATE_ABOVE), 1);
726 uint32_t values[] = {1};
727 xcb_change_window_attributes(xcb->connection, box_window,
728 XCB_CW_OVERRIDE_REDIRECT, values);
729 } else {
730 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_WINDOW_TYPE,
731 &(xcb->ewmh._NET_WM_WINDOW_TYPE_NORMAL), 1);
732 x11_disable_decoration(box_window);
733 }
734
735 TICK_N("setup window attributes");
736 XcbState.fullscreen =
737 rofi_theme_get_boolean(WIDGET(win), "fullscreen", FALSE);
738 if (XcbState.fullscreen) {
739 xcb_atom_t atoms[] = {xcb->ewmh._NET_WM_STATE_FULLSCREEN,
740 xcb->ewmh._NET_WM_STATE_ABOVE};
741 window_set_atom_prop(box_window, xcb->ewmh._NET_WM_STATE, atoms,
742 sizeof(atoms) / sizeof(xcb_atom_t));
743 }
744
745 xcb_atom_t protocols[] = {netatoms[WM_TAKE_FOCUS]};
746 xcb_icccm_set_wm_protocols(xcb->connection, box_window,
747 xcb->ewmh.WM_PROTOCOLS, G_N_ELEMENTS(protocols),
748 protocols);
749
750 TICK_N("setup window fullscreen");
751 // Set the WM_NAME
753 const char wm_class_name[] = "rofi\0Rofi";
754 xcb_icccm_set_wm_class(xcb->connection, box_window, sizeof(wm_class_name),
755 wm_class_name);
756
757 TICK_N("setup window name and class");
758 const char *transparency =
759 rofi_theme_get_string(WIDGET(win), "transparency", NULL);
760 if (transparency) {
762 }
763 if (xcb->sncontext != NULL) {
764 sn_launchee_context_setup_window(xcb->sncontext, CacheState.main_window);
765 }
766 TICK_N("setup startup notification");
767 widget_free(WIDGET(win));
768 TICK_N("done");
769
770 // Set the PID.
771 pid_t pid = getpid();
772 xcb_ewmh_set_wm_pid(&(xcb->ewmh), CacheState.main_window, pid);
773
774 // Get hostname
775 const char *hostname = g_get_host_name();
776 char *ahost = g_hostname_to_ascii(hostname);
777 if (ahost != NULL) {
778 xcb_icccm_set_wm_client_machine(xcb->connection, CacheState.main_window,
779 XCB_ATOM_STRING, 8, strlen(ahost), ahost);
780 g_free(ahost);
781 }
782}
783
790 if (XcbState.fullscreen) {
791 state->width = XcbState.mon.w;
792 return;
793 }
794 // Calculate as float to stop silly, big rounding down errors.
795 state->width = (XcbState.mon.w / 100.0f) * DEFAULT_MENU_WIDTH;
796 // Use theme configured width, if set.
798 "width", state->width);
800}
801
806static void
809 if (xce->window == CacheState.main_window) {
810 if (state->x != xce->x || state->y != xce->y) {
811 state->x = xce->x;
812 state->y = xce->y;
814 }
815 if (state->width != xce->width || state->height != xce->height) {
816 state->width = xce->width;
817 state->height = xce->height;
818
819 cairo_destroy(XcbState.edit_draw);
820 cairo_surface_destroy(XcbState.edit_surf);
821
822 xcb_free_pixmap(xcb->connection, XcbState.edit_pixmap);
823 XcbState.edit_pixmap = xcb_generate_id(xcb->connection);
824 xcb_create_pixmap(xcb->connection, depth->depth, XcbState.edit_pixmap,
825 CacheState.main_window, state->width, state->height);
826
827 XcbState.edit_surf =
828 cairo_xcb_surface_create(xcb->connection, XcbState.edit_pixmap,
829 visual, state->width, state->height);
830 XcbState.edit_draw = cairo_create(XcbState.edit_surf);
831 g_debug("Re-size window based external request: %d %d", state->width,
832 state->height);
833 widget_resize(WIDGET(state->main_window), state->width, state->height);
834 }
835 }
836}
837
842 xcb_window_t target) {
843 if ((CacheState.flags & MENU_NORMAL_WINDOW) == 0) {
844 if (target != CacheState.main_window) {
845 state->quit = TRUE;
846 state->retv = MENU_CANCEL;
847 }
848 }
849}
850
852 if (XcbState.repaint_source == 0) {
853 XcbState.count++;
854 g_debug("redraw %llu", XcbState.count);
855 XcbState.repaint_source = g_idle_add_full(
856 G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL);
857 }
858}
859
861 if (XcbState.fullscreen == TRUE) {
862 return XcbState.mon.h;
863 }
864
865 RofiDistance h =
866 rofi_theme_get_distance(WIDGET(state->main_window), "height", 0);
867 unsigned int height = distance_get_pixel(h, ROFI_ORIENTATION_VERTICAL);
868 // If height is set, return it.
869 if (height > 0) {
870 return height;
871 }
872 // Autosize based on widgets.
873 widget *main_window = WIDGET(state->main_window);
874 return widget_get_desired_height(main_window, state->width);
875}
876
877static void xcb_rofi_view_hide(void) {
878 if (CacheState.main_window != XCB_WINDOW_NONE) {
880 xcb_unmap_window(xcb->connection, CacheState.main_window);
882 }
883}
884
885static void xcb_rofi_view_cleanup(void) {
886 // Clear clipboard data.
888 g_debug("Cleanup.");
889 if (XcbState.idle_timeout > 0) {
890 g_source_remove(XcbState.idle_timeout);
891 XcbState.idle_timeout = 0;
892 }
893 if (CacheState.refilter_timeout > 0) {
894 g_source_remove(CacheState.refilter_timeout);
895 CacheState.refilter_timeout = 0;
896 }
897 if (CacheState.overlay_timeout) {
898 g_source_remove(CacheState.overlay_timeout);
899 CacheState.overlay_timeout = 0;
900 }
901 if (CacheState.user_timeout > 0) {
902 g_source_remove(CacheState.user_timeout);
903 CacheState.user_timeout = 0;
904 }
905 if (XcbState.repaint_source > 0) {
906 g_source_remove(XcbState.repaint_source);
907 XcbState.repaint_source = 0;
908 }
909 if (XcbState.fake_bg) {
910 cairo_surface_destroy(XcbState.fake_bg);
911 XcbState.fake_bg = NULL;
912 }
913 if (XcbState.edit_draw) {
914 cairo_destroy(XcbState.edit_draw);
915 XcbState.edit_draw = NULL;
916 }
917 if (XcbState.edit_surf) {
918 cairo_surface_destroy(XcbState.edit_surf);
919 XcbState.edit_surf = NULL;
920 }
921 if (CacheState.main_window != XCB_WINDOW_NONE) {
922 g_debug("Unmapping and free'ing window");
923 xcb_unmap_window(xcb->connection, CacheState.main_window);
924 xcb_free_gc(xcb->connection, XcbState.gc);
925 xcb_free_pixmap(xcb->connection, XcbState.edit_pixmap);
926 xcb_destroy_window(xcb->connection, CacheState.main_window);
927 CacheState.main_window = XCB_WINDOW_NONE;
928 }
929 if (map != XCB_COLORMAP_NONE) {
930 xcb_free_colormap(xcb->connection, map);
931 map = XCB_COLORMAP_NONE;
932 }
933 xcb_flush(xcb->connection);
934 g_assert(g_queue_is_empty(&(CacheState.views)));
935
937}
938
940 return CacheState.main_window;
941}
942
943static void xcb_rofi_view_set_window_title(const char *title) {
944 ssize_t len = strlen(title);
945 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
946 CacheState.main_window, xcb->ewmh._NET_WM_NAME,
947 xcb->ewmh.UTF8_STRING, 8, len, title);
948 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
949 CacheState.main_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING,
950 8, len, title);
951}
952
954 .update = xcb_rofi_view_update,
955 .temp_configure_notify = xcb_rofi_view_temp_configure_notify,
956 .temp_click_to_exit = xcb_rofi_view_temp_click_to_exit,
957 .frame_callback = xcb_rofi_view_frame_callback,
958 .queue_redraw = xcb_rofi_view_queue_redraw,
959
960 .set_window_title = xcb_rofi_view_set_window_title,
961 .calculate_window_position = xcb_rofi_view_calculate_window_position,
962 .calculate_window_width = xcb_rofi_view_calculate_window_width,
963 .calculate_window_height = xcb_rofi_view_calculate_window_height,
964 .window_update_size = xcb_rofi_view_window_update_size,
965 .set_cursor = xcb_rofi_view_set_cursor,
966 .ping_mouse = xcb_rofi_view_ping_mouse,
967
968 .cleanup = xcb_rofi_view_cleanup,
969 .hide = xcb_rofi_view_hide,
970 .reload = xcb_rofi_view_reload,
971
972 .__create_window = xcb___create_window,
973 .get_window = xcb_rofi_view_get_window,
974 .get_current_monitor = xcb_rofi_view_get_current_monitor,
975
976 .set_size = NULL,
977 .get_size = NULL,
978};
979
int monitor_active(workarea *mon)
Definition display.c:18
void display_early_cleanup(void)
Definition display.c:30
void display_revert_input_focus(void)
Definition display.c:22
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:466
char * rofi_expand_path(const char *input)
Definition helper.c:782
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:973
void textbox_text(textbox *tb, const char *text)
Definition textbox.c:403
int textbox_get_cursor_x_pos(const textbox *tb)
Definition textbox.c:1099
void rofi_view_queue_redraw(void)
Definition view.c:2129
RofiViewState * rofi_view_get_active(void)
Definition view.c:299
MenuFlags
Definition view.h:54
void rofi_view_handle_text(RofiViewState *state, char *text)
Definition view.c:1401
void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y, gboolean find_mouse_target)
Definition view.c:1435
@ MENU_NORMAL_WINDOW
Definition view.h:60
gboolean rofi_set_im_window_pos(int new_x, int new_y)
const view_proxy * xcb_view_proxy
Definition view.c:980
void input_history_save(void)
Definition view.c:526
void input_history_initialize(void)
Definition view.c:483
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:229
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:1406
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:2114
GThreadPool * tpool
Definition view.c:70
struct _rofi_view_cache_state CacheState
Definition view.c:75
guint idle_timeout
Definition view.c:74
static const int loc_transtable[9]
Definition view.c:121
unsigned long long count
Definition view.c:76
static view_proxy view_
Definition view.c:413
gboolean fullscreen
Definition view.c:80
guint repaint_source
Definition view.c:78
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:348
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:374
void x11_set_cursor(xcb_window_t window, X11CursorType type)
Definition display.c:1977
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:1911
void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms, int count)
Definition display.c:414
void xcb_stuff_set_clipboard(char *data)
Definition display.c:1989
void x11_disable_decoration(xcb_window_t window)
Definition display.c:1953
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 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:807
static void xcb_rofi_view_calculate_window_width(RofiViewState *state)
Definition view.c:789
static void xcb___create_window(MenuFlags menu_flags)
Definition view.c:608
GTimer * time
Definition view.c:159
static void xcb_rofi_view_window_update_size(RofiViewState *state)
Definition view.c:367
static void xcb_rofi_view_set_window_title(const char *title)
Definition view.c:943
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:476
static void xcb_rofi_view_cleanup(void)
Definition view.c:885
static struct @377203263163211155301074074321032144320117357172 XcbState
gboolean do_bench
Definition view.c:152
static void xcb_rofi_view_set_cursor(RofiCursorType type)
Definition view.c:411
xcb_gcontext_t gc
Definition view.c:107
static gboolean xcb_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data)
Definition view.c:439
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:280
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:860
int fake_bgrel
Definition view.c:115
static void xcb_rofi_view_reload(void)
Definition view.c:457
static void xcb_rofi_view_frame_callback(void)
Definition view.c:851
static struct @360144012110351074273012021035172042147021313275 BenchMark
X11CursorType cursor_type
Definition view.c:127
static xcb_window_t xcb_rofi_view_get_window(void)
Definition view.c:939
double min
Definition view.c:165
static void xcb_rofi_view_temp_click_to_exit(RofiViewState *state, xcb_window_t target)
Definition view.c:841
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:423
static void xcb_rofi_view_queue_redraw(void)
Definition view.c:464
static X11CursorType rofi_cursor_type_to_x11_cursor_type(RofiCursorType type)
Definition view.c:396
static void xcb_rofi_view_hide(void)
Definition view.c:877
X11CursorType
Definition xcb.h:140
@ CURSOR_POINTER
Definition xcb.h:144
@ CURSOR_DEFAULT
Definition xcb.h:142
@ CURSOR_TEXT
Definition xcb.h:146