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#include <errno.h>
33#include <locale.h>
34#include <signal.h>
35#include <stdint.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <time.h>
40#include <unistd.h>
41
42#include <cairo.h>
43
44#include "rofi.h"
45#include "settings.h"
46#include "timings.h"
47
48#include "display.h"
49#include "helper-theme.h"
50#include "helper.h"
51#include "mode.h"
52#include "modes/modes.h"
53
54#include "view-internal.h"
55#include "view.h"
56
57#include "theme.h"
58
65static void wayland_rofi_view_update(RofiViewState *state, gboolean qr);
66
67void process_result(RofiViewState *state);
71static struct {
75 guint idle_timeout;
77 unsigned long long count;
79 guint repaint_source;
81 gboolean fullscreen;
82
83 int monitor_width;
85
86 int surface_width;
88
89 int menu_width;
90 int menu_height;
91
92 int menu_x;
93 int menu_y;
94
95} WlState = {
96 .flags = MENU_NORMAL,
97 .idle_timeout = 0,
98 .count = 0L,
99 .repaint_source = 0,
100 .fullscreen = FALSE,
101 .monitor_width = 0,
102 .monitor_height = 0,
103
104 .surface_width = 0,
105 .surface_height = 0,
106
107 .menu_width = 0,
108 .menu_height = 0,
109
110 .menu_x = 0,
111 .menu_y = 0,
113
114static void wayland_rofi_view_get_current_monitor(int *width, int *height) {
115 // TODO: handle changing monitor resolution
116 if (WlState.monitor_width == 0 && WlState.monitor_height == 0) {
118 &WlState.monitor_height);
119 }
120
121 if (width) {
122 *width = WlState.monitor_width;
123 }
124 if (height) {
125 *height = WlState.monitor_height;
126 }
127}
128
129static gboolean wayland_rofi_view_repaint(G_GNUC_UNUSED void *data) {
131 if (state) {
132 // Repaint the view (if needed).
133 // After a resize the edit_pixmap surface might not contain anything
134 // anymore. If we already re-painted, this does nothing.
136 WlState.repaint_source = 0;
137 }
138 return G_SOURCE_REMOVE;
139}
140
145
147 G_GNUC_UNUSED RofiViewState *state) {}
148
150 return rofi_theme_get_position(WIDGET(state->main_window), "location",
152}
153
155 char *property = ori == ROFI_ORIENTATION_HORIZONTAL ? "x-offset" : "y-offset";
156
157 RofiDistance offset =
158 rofi_theme_get_distance(WIDGET(state->main_window), property, 0);
159 return distance_get_pixel(offset, ori);
160}
161
162static void window_update_size_normal(RofiViewState *state, int offset_x,
163 int offset_y) {
164 WlState.surface_width = state->width;
165 WlState.surface_height = state->height;
166 WlState.menu_x = 0;
167 WlState.menu_y = 0;
168
169 display_set_surface_dimensions(state->width, state->height, offset_x,
170 offset_y, rofi_get_location(state));
171}
172
174 int offset_x, int offset_y) {
175 int screen_width = 0;
176 int screen_height = 0;
177 int loc = rofi_get_location(state);
178
179 wayland_rofi_view_get_current_monitor(&screen_width, &screen_height);
180
181 if (screen_width <= 0 || screen_height <= 0) {
182 screen_width = state->width;
183 screen_height = state->height;
184 }
185
186 WlState.surface_width = screen_width;
187 WlState.surface_height = screen_height;
188
189 switch (loc) {
190 case WL_NORTH_WEST:
191 WlState.menu_x = offset_x;
192 WlState.menu_y = offset_y;
193 break;
194 case WL_NORTH:
195 WlState.menu_x =
196 (WlState.surface_width - WlState.menu_width) / 2 + offset_x;
197 WlState.menu_y = offset_y;
198 break;
199 case WL_NORTH_EAST:
200 WlState.menu_x = WlState.surface_width - WlState.menu_width - offset_x;
201 WlState.menu_y = offset_y;
202 break;
203 case WL_EAST:
204 WlState.menu_x = WlState.surface_width - WlState.menu_width - offset_x;
205 WlState.menu_y =
206 (WlState.surface_height - WlState.menu_height) / 2 + offset_y;
207 break;
208 case WL_SOUTH_EAST:
209 WlState.menu_x = WlState.surface_width - WlState.menu_width - offset_x;
210 WlState.menu_y = WlState.surface_height - WlState.menu_height - offset_y;
211 break;
212 case WL_SOUTH:
213 WlState.menu_x =
214 (WlState.surface_width - WlState.menu_width) / 2 + offset_x;
215 WlState.menu_y = WlState.surface_height - WlState.menu_height - offset_y;
216 break;
217 case WL_SOUTH_WEST:
218 WlState.menu_x = offset_x;
219 WlState.menu_y = WlState.surface_height - WlState.menu_height - offset_y;
220 break;
221 case WL_WEST:
222 WlState.menu_x = offset_x;
223 WlState.menu_y =
224 (WlState.surface_height - WlState.menu_height) / 2 + offset_y;
225 break;
226 case WL_CENTER:
227 default:
228 WlState.menu_x =
229 (WlState.surface_width - WlState.menu_width) / 2 + offset_x;
230 WlState.menu_y =
231 (WlState.surface_height - WlState.menu_height) / 2 + offset_y;
232 break;
233 }
234
235 /* Click-capture is limited to the current output. If the menu is larger than
236 * the output and visually overflows onto another output, the overflowed area
237 * will not be part of the capture surface and clicks there will not trigger
238 * click-to-exit.
239 */
240 display_set_surface_dimensions(WlState.surface_width, WlState.surface_height,
241 0, 0, WL_NORTH_WEST);
242}
243
245 if (state == NULL) {
246 return;
247 }
248
249 int offset_x = rofi_get_offset_px(state, ROFI_ORIENTATION_HORIZONTAL);
250 int offset_y = rofi_get_offset_px(state, ROFI_ORIENTATION_VERTICAL);
251
252 widget_resize(WIDGET(state->main_window), state->width, state->height);
253
254 WlState.menu_width = state->width;
255 WlState.menu_height = state->height;
256
257 if (config.click_to_exit) {
258 window_update_size_with_outside_click(state, offset_x, offset_y);
259 } else {
260 window_update_size_normal(state, offset_x, offset_y);
261 }
263}
264
265static void wayland_rofi_view_set_size(RofiViewState *state, gint width,
266 gint height) {
267 if (width > -1) {
268 state->width = width;
269 }
270 if (height > -1) {
271 state->height = height;
272 }
274}
275
276static void wayland_rofi_view_get_size(RofiViewState *state, gint *width,
277 gint *height) {
278 *width = state->width;
279 *height = state->height;
280}
281
282static void wayland_rofi_view_ping_mouse(RofiViewState *state) { (void)state; }
283
284static gboolean wayland_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data) {
286
287 /* Clear before refiltering: a reload signalled during the pass, such as a
288 * finished icon fetch, would otherwise coalesce into it and be lost. */
289 WlState.idle_timeout = 0;
290
291 if (state) {
292 // For UI update on this.
293 if (state->tb_total_rows) {
294 char *r = g_strdup_printf("%u", mode_get_num_entries(state->sw));
295 textbox_text(state->tb_total_rows, r);
296 g_free(r);
297 }
298 state->reload = TRUE;
299 state->refilter = TRUE;
300
302 }
303 return G_SOURCE_REMOVE;
304}
305
306static void wayland_rofi_view_reload(void) {
307 // @TODO add check if current view is equal to the callee
308 if (WlState.idle_timeout == 0) {
309 WlState.idle_timeout =
310 g_timeout_add(1000 / 15, wayland_rofi_view_reload_idle, NULL);
311 }
312}
315 if (state && WlState.repaint_source == 0) {
316 WlState.count++;
317 g_debug("redraw %llu", WlState.count);
318
320
321 WlState.repaint_source = g_idle_add_full(
322 G_PRIORITY_HIGH_IDLE, wayland_rofi_view_repaint, NULL, NULL);
323 }
324}
325
326static void wayland___create_window(MenuFlags menu_flags) {
327
329
330 TICK_N("create cairo surface");
331 PangoContext *p = pango_context_new();
332 pango_context_set_font_map(p, pango_cairo_font_map_get_default());
333 TICK_N("pango cairo font setup");
334
335 WlState.flags = menu_flags;
336
337 // Setup dpi
338 PangoFontMap *font_map = pango_cairo_font_map_get_default();
339 if (config.dpi > 1) {
340 pango_cairo_font_map_set_resolution((PangoCairoFontMap *)font_map,
341 (double)config.dpi);
342 } else if (config.dpi == 0 || config.dpi == 1) {
343 // This is an heuristic that works well for simple cases (eg single monitor)
344 // Accurate dpi information only comes after we display the first surface on
345 // the screen when it's too late to use for metric units.
346 double dpi = wayland_get_dpi_estimation();
347 if (dpi >= 0) {
348 config.dpi = dpi;
349 pango_cairo_font_map_set_resolution((PangoCairoFontMap *)font_map, dpi);
350 } else {
351 g_warning(
352 "DPI auto-detect failed, the output is not known yet or does not "
353 "provide a physical size");
354 config.dpi =
355 pango_cairo_font_map_get_resolution((PangoCairoFontMap *)font_map);
356 }
357 } else {
358 // default pango is 96.
359 config.dpi =
360 pango_cairo_font_map_get_resolution((PangoCairoFontMap *)font_map);
361 }
362 // Setup font.
363 // Dummy widget.
364 box *win = box_create(NULL, "window", ROFI_ORIENTATION_HORIZONTAL);
365 const char *font =
367 if (font) {
368 PangoFontDescription *pfd = pango_font_description_from_string(font);
369 if (helper_validate_font(pfd, font)) {
370 pango_context_set_font_description(p, pfd);
371 }
372 pango_font_description_free(pfd);
373 }
374 PangoLanguage *l = pango_language_get_default();
375 pango_context_set_language(p, l);
376 TICK_N("configure font");
377
378 // Tell textbox to use this context.
380 // cleanup
381 g_object_unref(p);
382
383 WlState.fullscreen = rofi_theme_get_boolean(WIDGET(win), "fullscreen", FALSE);
384
385 if (WlState.fullscreen) {
387 }
388
389 widget_free(WIDGET(win));
390
391 TICK_N("done");
392}
393
400 int screen_width = 1920;
401 display_get_surface_dimensions(&screen_width, NULL);
402
403 if (WlState.fullscreen == TRUE) {
404 state->width = screen_width;
405 return;
406 }
407
408 // Calculate as float to stop silly, big rounding down errors.
409 state->width = (screen_width / 100.0f) * DEFAULT_MENU_WIDTH;
410 // Use theme configured width, if set.
412 "width", state->width);
414}
415
416static void wayland_rofi_view_update(RofiViewState *state, gboolean qr) {
417 if (!widget_need_redraw(WIDGET(state->main_window))) {
418 return;
419 }
420 g_debug("Redraw view");
421 TICK();
422
423 int buffer_width = state->width;
424 int buffer_height = state->height;
425
426 if (config.click_to_exit) {
427 buffer_width = WlState.surface_width;
428 buffer_height = WlState.surface_height;
429 }
430
431 if (state->pool == NULL) {
432 state->pool = display_buffer_pool_new(buffer_width, buffer_height);
433 }
434
435 cairo_surface_t *surface = display_buffer_pool_get_next_buffer(state->pool);
436 if (surface == NULL) {
437 // no available buffer, bail out
438 return;
439 }
440
441 cairo_t *d = cairo_create(surface);
442 cairo_set_operator(d, CAIRO_OPERATOR_SOURCE);
443
444 // Paint the background transparent.
445 cairo_set_source_rgba(d, 0, 0, 0, 0);
446 guint scale = display_scale();
447 cairo_surface_set_device_scale(surface, scale, scale);
448 cairo_paint(d);
449 TICK_N("Background");
450
451 // Always paint as overlay over the background.
452 cairo_set_operator(d, CAIRO_OPERATOR_OVER);
453
454 if (config.click_to_exit) {
455 g_debug("draw capture mode: surface=%dx%d menu=%dx%d pos=(%d,%d)",
456 WlState.surface_width, WlState.surface_height, WlState.menu_width,
457 WlState.menu_height, WlState.menu_x, WlState.menu_y);
458 cairo_translate(d, WlState.menu_x, WlState.menu_y);
459 } else {
460 g_debug("draw normal mode: surface=%dx%d", buffer_width, buffer_height);
461 }
462
463 widget_draw(WIDGET(state->main_window), d);
464
465 TICK_N("widgets");
466 cairo_destroy(d);
467 display_surface_commit(surface);
468
469 if (qr) {
471 }
472}
473
475 if (WlState.repaint_source == 0) {
476 WlState.repaint_source = g_idle_add_full(
477 G_PRIORITY_HIGH_IDLE, wayland_rofi_view_repaint, NULL, NULL);
478 }
479}
480
482 if (WlState.fullscreen == TRUE) {
483 int height = 1080;
484 display_get_surface_dimensions(NULL, &height);
485 return height;
486 }
487
488 RofiDistance h =
489 rofi_theme_get_distance(WIDGET(state->main_window), "height", 0);
490 unsigned int height = distance_get_pixel(h, ROFI_ORIENTATION_VERTICAL);
491 // If height is set, return it.
492 if (height > 0) {
493 return height;
494 }
495 // Autosize based on widgets.
496 widget *main_window = WIDGET(state->main_window);
497
498 return widget_get_desired_height(main_window, state->width);
499}
500
502
503static void wayland_rofi_view_cleanup(void) {
504 g_debug("Cleanup.");
505 if (WlState.idle_timeout > 0) {
506 g_source_remove(WlState.idle_timeout);
507 WlState.idle_timeout = 0;
508 }
510 g_source_remove(CacheState.refilter_timeout);
512 }
514 g_source_remove(CacheState.overlay_timeout);
516 }
517 if (CacheState.user_timeout > 0) {
518 g_source_remove(CacheState.user_timeout);
520 }
521 if (WlState.repaint_source > 0) {
522 g_source_remove(WlState.repaint_source);
523 WlState.repaint_source = 0;
524 }
525
527}
528
529static void
530wayland_rofi_view_set_window_title(G_GNUC_UNUSED const char *title) {}
531
534 if (state == NULL) {
535 return;
536 }
537 display_buffer_pool_free(state->pool);
538 state->pool = NULL;
539 wayland_rofi_view_update(state, TRUE);
540}
541
542static void wayland_rofi_view_get_menu_rect(int *x, int *y, int *w, int *h) {
543 if (x)
544 *x = WlState.menu_x;
545 if (y)
546 *y = WlState.menu_y;
547 if (w)
548 *w = WlState.menu_width;
549 if (h)
550 *h = WlState.menu_height;
551}
552
554 .update = wayland_rofi_view_update,
555 .temp_configure_notify = NULL,
556 .temp_click_to_exit = NULL,
557 .frame_callback = wayland_rofi_view_frame_callback,
558 .queue_redraw = wayland_rofi_view_queue_redraw,
559
560 .set_window_title = wayland_rofi_view_set_window_title,
561 .calculate_window_position = wayland_rofi_view_calculate_window_position,
562 .calculate_window_height = wayland_rofi_view_calculate_window_height,
563 .calculate_window_width = wayland_rofi_view_calculate_window_width,
564 .window_update_size = wayland_rofi_view_window_update_size,
566 .ping_mouse = wayland_rofi_view_ping_mouse,
567
568 .cleanup = wayland_rofi_view_cleanup,
570 .reload = wayland_rofi_view_reload,
571
572 .__create_window = wayland___create_window,
573 .get_window = NULL,
574 .get_current_monitor = wayland_rofi_view_get_current_monitor,
575
576 .set_size = wayland_rofi_view_set_size,
577 .get_size = wayland_rofi_view_get_size,
578
579 .pool_refresh = wayland_rofi_view_pool_refresh,
580
581 .get_menu_rect = wayland_rofi_view_get_menu_rect,
582};
583
void display_early_cleanup(void)
Definition display.c:52
guint display_scale(void)
Definition display.c:78
void display_set_fullscreen_mode(void)
Definition display.c:92
@ 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
unsigned int mode_get_num_entries(const Mode *mode)
Definition mode.c:70
#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
RofiViewState * rofi_view_get_active(void)
Definition view.c:321
MenuFlags
Definition view.h:55
void rofi_view_maybe_update(RofiViewState *state)
Definition view.c:1560
@ MENU_NORMAL
Definition view.h:57
void input_history_save(void)
Definition view.c:580
void input_history_initialize(void)
Definition view.c:537
void rofi_view_pool_refresh(void)
Definition view.c:2243
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
struct _widget widget
Definition widget.h:49
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
RofiOrientation
Definition rofi-types.h:139
@ ROFI_ORIENTATION_HORIZONTAL
Definition rofi-types.h:141
@ ROFI_ORIENTATION_VERTICAL
Definition rofi-types.h:140
void process_result(RofiViewState *state)
Definition rofi.c:235
Settings config
#define DEFAULT_MENU_WIDTH
Definition settings.h:235
textbox * tb_total_rows
WindowLocation location
Definition settings.h:110
int click_to_exit
Definition settings.h:178
char * menu_font
Definition settings.h:65
int dpi
Definition settings.h:171
unsigned long long count
gboolean fullscreen
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
struct _rofi_view_cache_state CacheState
Definition view.c:75
double wayland_get_dpi_estimation(void)
Definition display.c:1556
void display_surface_commit(cairo_surface_t *surface)
Definition display.c:343
cairo_surface_t * display_buffer_pool_get_next_buffer(wayland_buffer_pool *pool)
Definition display.c:320
gboolean display_get_surface_dimensions(int *width, int *height)
Definition display.c:2020
void wayland_display_set_cursor_type(RofiCursorType type)
Definition display.c:835
void display_buffer_pool_free(wayland_buffer_pool *self)
Definition display.c:264
void display_set_surface_dimensions(int width, int height, int x_margin, int y_margin, int loc)
Definition display.c:2038
wayland_buffer_pool * display_buffer_pool_new(gint width, gint height)
Definition display.c:194
static int rofi_get_offset_px(RofiViewState *state, RofiOrientation ori)
Definition view.c:154
static void wayland_rofi_view_get_size(RofiViewState *state, gint *width, gint *height)
Definition view.c:276
const view_proxy * wayland_view_proxy
Definition view.c:584
static int rofi_get_location(RofiViewState *state)
Definition view.c:149
static gboolean wayland_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data)
Definition view.c:284
static void wayland_rofi_view_get_menu_rect(int *x, int *y, int *w, int *h)
Definition view.c:542
static void wayland_rofi_view_hide(void)
Definition view.c:501
static void wayland_rofi_view_set_window_title(G_GNUC_UNUSED const char *title)
Definition view.c:530
static void wayland_rofi_view_calculate_window_width(RofiViewState *state)
Definition view.c:399
static void wayland_rofi_view_calculate_window_position(G_GNUC_UNUSED RofiViewState *state)
Definition view.c:146
static void wayland_rofi_view_reload(void)
Definition view.c:306
static int wayland_rofi_view_calculate_window_height(RofiViewState *state)
Definition view.c:481
static void wayland_rofi_view_set_size(RofiViewState *state, gint width, gint height)
Definition view.c:265
static struct @303345143117023230245122030172270246040000354217 WlState
static const int loc_transtable[9]
Definition view.c:141
static void wayland_rofi_view_cleanup(void)
Definition view.c:503
static void wayland_rofi_view_frame_callback(void)
Definition view.c:474
static void wayland_rofi_view_get_current_monitor(int *width, int *height)
Definition view.c:114
static void wayland_rofi_view_ping_mouse(RofiViewState *state)
Definition view.c:282
static void wayland_rofi_view_window_update_size(RofiViewState *state)
Definition view.c:244
static void wayland___create_window(MenuFlags menu_flags)
Definition view.c:326
static view_proxy view_
Definition view.c:553
static void wayland_rofi_view_queue_redraw(void)
Definition view.c:313
static gboolean wayland_rofi_view_repaint(G_GNUC_UNUSED void *data)
Definition view.c:129
static void wayland_rofi_view_update(RofiViewState *state, gboolean qr)
Definition view.c:416
static void wayland_rofi_view_pool_refresh(void)
Definition view.c:532
static void window_update_size_with_outside_click(RofiViewState *state, int offset_x, int offset_y)
Definition view.c:173
static void window_update_size_normal(RofiViewState *state, int offset_x, int offset_y)
Definition view.c:162