Skip to content

Commit 00a2ccd

Browse files
committed
wayland: close rofi on outside click in capture mode
Handle click-to-exit natively in the Wayland pointer path. When capture mode is enabled, pointer events are compared against the visible menu rectangle. Pointer presses outside that rectangle cancel the active view, while inside clicks continue through the normal widget event path. This allows click-to-exit to work on Wayland without compositor-side scripts or hacks. Fix doxygen and remove stray comment Seperated outside-click handling into its own function
1 parent d4eeca5 commit 00a2ccd

File tree

3 files changed

+114
-95
lines changed

3 files changed

+114
-95
lines changed

include/view.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,12 @@ void rofi_view_ellipsize_listview(RofiViewState *state,
408408
*/
409409
gboolean rofi_set_im_window_pos(int new_x, int new_y);
410410

411-
// Wayland click-to-exit helper
411+
// Cancel the current view
412+
void rofi_view_cancel(RofiViewState *state);
412413

414+
// Wayland click-to-exit helper
413415
gboolean wayland_rofi_view_capture_click_outside_enabled(void);
414416
void wayland_rofi_view_get_menu_rect(int *x, int *y, int *w, int *h);
415-
void wayland_rofi_view_cancel(RofiViewState *state);
416417

417418
/**
418419
* @param wid to test.

source/wayland/display.c

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2323
*/
2424

25+
#include <stdio.h>
26+
2527
#define G_LOG_DOMAIN "Wayland"
2628
#pragma GCC diagnostic ignored "-Wunused-variable"
2729
#pragma GCC diagnostic ignored "-Wunused-parameter"
@@ -65,8 +67,8 @@
6567
#endif
6668
#include "keyboard-shortcuts-inhibit-unstable-v1-protocol.h"
6769
#include "primary-selection-unstable-v1-protocol.h"
68-
#include "wlr-layer-shell-unstable-v1-protocol.h"
6970
#include "text-input-unstable-v3-protocol.h"
71+
#include "wlr-layer-shell-unstable-v1-protocol.h"
7072

7173
#define wayland_output_get_dpi(output, scale, dimension) \
7274
((output)->current.physical_##dimension > 0 && (scale) > 0 \
@@ -581,8 +583,26 @@ static void wayland_pointer_send_events(wayland_seat *self) {
581583
return;
582584
}
583585

586+
gboolean capture = wayland_rofi_view_capture_click_outside_enabled();
587+
int menu_x = 0, menu_y = 0, menu_w = 0, menu_h = 0;
588+
589+
if (capture) {
590+
wayland_rofi_view_get_menu_rect(&menu_x, &menu_y, &menu_w, &menu_h);
591+
if (menu_w <= 0 || menu_h <= 0) {
592+
capture = FALSE;
593+
}
594+
}
595+
584596
if (self->motion.x > -1 || self->motion.y > -1) {
585-
rofi_view_handle_mouse_motion(state, self->motion.x, self->motion.y,
597+
int motion_x = self->motion.x;
598+
int motion_y = self->motion.y;
599+
600+
if (capture) {
601+
motion_x -= menu_x;
602+
motion_y -= menu_y;
603+
}
604+
605+
rofi_view_handle_mouse_motion(state, motion_x, motion_y,
586606
config.hover_select);
587607
self->motion.x = -1;
588608
self->motion.y = -1;
@@ -602,17 +622,43 @@ static void wayland_pointer_send_events(wayland_seat *self) {
602622
}
603623

604624
if (self->button.button > 0) {
625+
gboolean inside = TRUE;
626+
627+
if (capture) {
628+
inside = self->button.x >= menu_x && self->button.x < menu_x + menu_w &&
629+
self->button.y >= menu_y && self->button.y < menu_y + menu_h;
630+
}
631+
605632
if (self->button.pressed) {
606-
rofi_view_handle_mouse_motion(state, self->button.x, self->button.y,
607-
FALSE);
633+
if (capture && !inside) {
634+
635+
wayland_rofi_view_cancel(state);
636+
637+
self->button.button = 0;
638+
rofi_view_maybe_update(state);
639+
return;
640+
}
641+
642+
int button_x = self->button.x;
643+
int button_y = self->button.y;
644+
645+
if (capture) {
646+
button_x -= menu_x;
647+
button_y -= menu_y;
648+
}
649+
650+
rofi_view_handle_mouse_motion(state, button_x, button_y, FALSE);
608651
nk_bindings_seat_handle_button(wayland->bindings_seat, NULL, button,
609652
NK_BINDINGS_BUTTON_STATE_PRESS,
610653
self->button.time);
611654
} else {
612-
nk_bindings_seat_handle_button(wayland->bindings_seat, NULL, button,
613-
NK_BINDINGS_BUTTON_STATE_RELEASE,
614-
self->button.time);
655+
if (!capture || inside) {
656+
nk_bindings_seat_handle_button(wayland->bindings_seat, NULL, button,
657+
NK_BINDINGS_BUTTON_STATE_RELEASE,
658+
self->button.time);
659+
}
615660
}
661+
616662
self->button.button = 0;
617663
}
618664

@@ -1201,9 +1247,9 @@ static void update_cursor_rectangle(struct zwp_text_input_v3 *text_input) {
12011247
static void text_input_enter(void *data, struct zwp_text_input_v3 *text_input,
12021248
struct wl_surface *surface) {
12031249
zwp_text_input_v3_enable(text_input);
1204-
zwp_text_input_v3_set_content_type(
1205-
text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE,
1206-
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL);
1250+
zwp_text_input_v3_set_content_type(text_input,
1251+
ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE,
1252+
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL);
12071253
update_cursor_rectangle(text_input);
12081254
zwp_text_input_v3_commit(text_input);
12091255
}
@@ -1234,11 +1280,9 @@ static void text_input_commit_string(void *data,
12341280
}
12351281
}
12361282

1237-
static void
1238-
text_input_delete_surrounding_text(void *data,
1239-
struct zwp_text_input_v3 *text_input,
1240-
uint32_t before_length,
1241-
uint32_t after_length) {}
1283+
static void text_input_delete_surrounding_text(
1284+
void *data, struct zwp_text_input_v3 *text_input, uint32_t before_length,
1285+
uint32_t after_length) {}
12421286

12431287
static void text_input_done(void *data, struct zwp_text_input_v3 *text_input,
12441288
uint32_t serial) {}
@@ -1287,18 +1331,18 @@ static wayland_output *wayland_output_by_name(const char *name) {
12871331
}
12881332
double wayland_get_dpi_estimation(void) {
12891333
double retv = -1.0;
1290-
if ( wayland == 0 ) {
1334+
if (wayland == 0) {
12911335
return -1.0;
12921336
}
12931337
gsize noutputs = g_hash_table_size(wayland->outputs);
1294-
if ( noutputs == 1) {
1338+
if (noutputs == 1) {
12951339
GHashTableIter iter;
12961340
wayland_output *output;
12971341
g_hash_table_iter_init(&iter, wayland->outputs);
12981342
if (g_hash_table_iter_next(&iter, NULL, (gpointer *)&output)) {
12991343
return wayland_output_get_dpi(output, output->current.scale, height);
13001344
}
1301-
} else if (noutputs > 1 && config.monitor != NULL ) {
1345+
} else if (noutputs > 1 && config.monitor != NULL) {
13021346
wayland_output *output = wayland_output_by_name(config.monitor);
13031347
if (output != NULL) {
13041348
return wayland_output_get_dpi(output, output->current.scale, height);
@@ -1708,7 +1752,8 @@ static gboolean wayland_display_late_setup(void) {
17081752
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
17091753
} else {
17101754
layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
1711-
g_warning("Unknown wayland layer: %s, using default overlay", config.wayland_layer);
1755+
g_warning("Unknown wayland layer: %s, using default overlay",
1756+
config.wayland_layer);
17121757
}
17131758
wayland->wlr_surface = zwlr_layer_shell_v1_get_layer_surface(
17141759
wayland->layer_shell, wayland->surface, wlo, layer, "rofi");
@@ -1915,8 +1960,8 @@ static void wayland_get_clipboard_data(int cb_type, ClipboardCb callback,
19151960
if (cb_type == CLIPBOARD_DEFAULT) {
19161961
wl_data_offer_receive(clipboard->offer, "text/plain;charset=utf-8", fds[1]);
19171962
} else {
1918-
zwp_primary_selection_offer_v1_receive(clipboard->offer, "text/plain;charset=utf-8",
1919-
fds[1]);
1963+
zwp_primary_selection_offer_v1_receive(clipboard->offer,
1964+
"text/plain;charset=utf-8", fds[1]);
19201965
}
19211966
close(fds[1]);
19221967

source/wayland/view.c

Lines changed: 45 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ static struct {
8383
int monitor_width;
8484
int monitor_height;
8585

86-
gboolean capture_click_outside;
87-
8886
int surface_width;
8987
int surface_height;
9088

@@ -102,6 +100,15 @@ static struct {
102100
.fullscreen = FALSE,
103101
.monitor_width = 0,
104102
.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,
105112
};
106113

107114
static void wayland_rofi_view_get_current_monitor(int *width, int *height) {
@@ -152,52 +159,38 @@ static int rofi_get_offset_px(RofiViewState *state, RofiOrientation ori) {
152159
return distance_get_pixel(offset, ori);
153160
}
154161

155-
static void wayland_rofi_view_window_update_size(RofiViewState *state) {
156-
if (state == NULL) {
157-
return;
158-
}
159-
160-
int offset_x = rofi_get_offset_px(state, ROFI_ORIENTATION_HORIZONTAL);
161-
int offset_y = rofi_get_offset_px(state, ROFI_ORIENTATION_VERTICAL);
162-
163-
/* The widget itself should always keep the visible menu size. */
164-
widget_resize(WIDGET(state->main_window), state->width, state->height);
165-
166-
/* Store visible menu size. */
167-
WlState.menu_width = state->width;
168-
WlState.menu_height = state->height;
169-
170-
if (!WlState.capture_click_outside) {
171-
WlState.surface_width = state->width;
172-
WlState.surface_height = state->height;
173-
WlState.menu_x = 0;
174-
WlState.menu_y = 0;
175-
176-
g_debug("normal mode: surface=%dx%d menu=%dx%d", WlState.surface_width,
177-
WlState.surface_height, WlState.menu_width, WlState.menu_height);
162+
static 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;
178168

179-
display_set_surface_dimensions(state->width, state->height, offset_x,
180-
offset_y, rofi_get_location(state));
181-
rofi_view_pool_refresh();
182-
return;
183-
}
169+
/* Click-capture is limited to the current output. If the menu is larger than
170+
* the output and visually overflows onto another output, the overflowed area
171+
* will not be part of the capture surface and clicks there will not trigger
172+
* click-to-exit.
173+
*/
174+
display_set_surface_dimensions(state->width, state->height, offset_x,
175+
offset_y, rofi_get_location(state));
176+
}
184177

185-
/* Capture mode: surface becomes fullscreen, menu stays normal size. */
178+
static void window_update_size_with_outside_click(RofiViewState *state,
179+
int offset_x, int offset_y) {
186180
int screen_width = 0;
187181
int screen_height = 0;
182+
int loc = rofi_get_location(state);
183+
188184
wayland_rofi_view_get_current_monitor(&screen_width, &screen_height);
189185

190186
if (screen_width <= 0 || screen_height <= 0) {
191-
/* Fallback so we do not create a broken surface. */
192187
screen_width = state->width;
193188
screen_height = state->height;
194189
}
195190

196191
WlState.surface_width = screen_width;
197192
WlState.surface_height = screen_height;
198193

199-
int loc = rofi_get_location(state);
200-
201194
switch (loc) {
202195
case WL_NORTH_WEST:
203196
WlState.menu_x = offset_x;
@@ -244,26 +237,29 @@ static void wayland_rofi_view_window_update_size(RofiViewState *state) {
244237
break;
245238
}
246239

247-
/* Keep the actual layer surface fullscreen for outside-click capture. */
248240
display_set_surface_dimensions(WlState.surface_width, WlState.surface_height,
249241
0, 0, WL_NORTH_WEST);
250-
251-
g_debug("capture mode: surface=%dx%d menu=%dx%d pos=(%d,%d) loc=%d "
252-
"offsets=(%d,%d)",
253-
WlState.surface_width, WlState.surface_height, WlState.menu_width,
254-
WlState.menu_height, WlState.menu_x, WlState.menu_y, loc, offset_x,
255-
offset_y);
256-
257-
rofi_view_pool_refresh();
258242
}
259243

260-
void wayland_rofi_view_cancel(RofiViewState *state) {
244+
static void wayland_rofi_view_window_update_size(RofiViewState *state) {
261245
if (state == NULL) {
262246
return;
263247
}
264248

265-
state->retv = MENU_CANCEL;
266-
state->quit = TRUE;
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+
}
262+
rofi_view_pool_refresh();
267263
}
268264

269265
static void wayland_rofi_view_set_size(RofiViewState *state, gint width,
@@ -335,15 +331,6 @@ static void wayland___create_window(MenuFlags menu_flags) {
335331

336332
WlState.flags = menu_flags;
337333

338-
/* Reset Wayland-local click-capture state for this view instance. */
339-
WlState.capture_click_outside = config.click_to_exit ? TRUE : FALSE;
340-
WlState.surface_width = 0;
341-
WlState.surface_height = 0;
342-
WlState.menu_width = 0;
343-
WlState.menu_height = 0;
344-
WlState.menu_x = 0;
345-
WlState.menu_y = 0;
346-
347334
// Setup dpi
348335
PangoFontMap *font_map = pango_cairo_font_map_get_default();
349336
if (config.dpi > 1) {
@@ -433,7 +420,7 @@ static void wayland_rofi_view_update(RofiViewState *state, gboolean qr) {
433420
int buffer_width = state->width;
434421
int buffer_height = state->height;
435422

436-
if (WlState.capture_click_outside) {
423+
if (config.click_to_exit) {
437424
buffer_width = WlState.surface_width;
438425
buffer_height = WlState.surface_height;
439426
}
@@ -461,7 +448,7 @@ static void wayland_rofi_view_update(RofiViewState *state, gboolean qr) {
461448
// Always paint as overlay over the background.
462449
cairo_set_operator(d, CAIRO_OPERATOR_OVER);
463450

464-
if (WlState.capture_click_outside) {
451+
if (config.click_to_exit) {
465452
g_debug("draw capture mode: surface=%dx%d menu=%dx%d pos=(%d,%d)",
466453
WlState.surface_width, WlState.surface_height, WlState.menu_width,
467454
WlState.menu_height, WlState.menu_x, WlState.menu_y);
@@ -481,12 +468,6 @@ static void wayland_rofi_view_update(RofiViewState *state, gboolean qr) {
481468
}
482469
}
483470

484-
/**
485-
* @param state The Menu Handle
486-
*
487-
* Check if a finalize function is set, and if sets executes it.
488-
*/
489-
490471
static void wayland_rofi_view_frame_callback(void) {
491472
if (WlState.repaint_source == 0) {
492473
WlState.repaint_source = g_idle_add_full(
@@ -539,14 +520,6 @@ static void wayland_rofi_view_cleanup(void) {
539520
WlState.repaint_source = 0;
540521
}
541522

542-
WlState.capture_click_outside = FALSE;
543-
WlState.surface_width = 0;
544-
WlState.surface_height = 0;
545-
WlState.menu_width = 0;
546-
WlState.menu_height = 0;
547-
WlState.menu_x = 0;
548-
WlState.menu_y = 0;
549-
550523
input_history_save();
551524
}
552525

@@ -564,7 +537,7 @@ static void wayland_rofi_view_pool_refresh(void) {
564537
}
565538

566539
gboolean wayland_rofi_view_capture_click_outside_enabled(void) {
567-
return WlState.capture_click_outside;
540+
return config.click_to_exit;
568541
}
569542

570543
void wayland_rofi_view_get_menu_rect(int *x, int *y, int *w, int *h) {

0 commit comments

Comments
 (0)