mirror of
https://github.com/labwc/labwc.git
synced 2026-02-06 04:06:33 -05:00
Merge pull request #533 from jlindgren90/fix-gtk-menus
cursor: Fix GTK3 menus when keeping the button pressed
This commit is contained in:
commit
89890b6be9
7 changed files with 113 additions and 43 deletions
|
|
@ -115,6 +115,7 @@ struct seat {
|
|||
struct view *view;
|
||||
struct wlr_scene_node *node;
|
||||
struct wlr_surface *surface;
|
||||
struct wlr_surface *toplevel;
|
||||
} pressed;
|
||||
|
||||
struct wl_client *active_client_while_inhibited;
|
||||
|
|
@ -152,6 +153,7 @@ struct seat {
|
|||
struct wl_listener destroy_drag;
|
||||
struct wl_listener constraint_commit;
|
||||
struct wl_listener idle_inhibitor_create;
|
||||
struct wl_listener pressed_surface_destroy;
|
||||
};
|
||||
|
||||
struct lab_data_buffer;
|
||||
|
|
@ -561,6 +563,9 @@ void seat_finish(struct server *server);
|
|||
void seat_reconfigure(struct server *server);
|
||||
void seat_focus_surface(struct seat *seat, struct wlr_surface *surface);
|
||||
void seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer);
|
||||
void seat_set_pressed(struct seat *seat, struct view *view,
|
||||
struct wlr_scene_node *node, struct wlr_surface *surface,
|
||||
struct wlr_surface *toplevel);
|
||||
void seat_reset_pressed(struct seat *seat);
|
||||
|
||||
void interactive_begin(struct view *view, enum input_mode mode,
|
||||
|
|
|
|||
77
src/cursor.c
77
src/cursor.c
|
|
@ -45,6 +45,32 @@ is_surface(enum ssd_part_type view_area)
|
|||
;
|
||||
}
|
||||
|
||||
static struct wlr_surface *
|
||||
get_toplevel(struct wlr_surface *surface)
|
||||
{
|
||||
while (surface && wlr_surface_is_xdg_surface(surface)) {
|
||||
struct wlr_xdg_surface *xdg_surface =
|
||||
wlr_xdg_surface_from_wlr_surface(surface);
|
||||
if (!xdg_surface) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (xdg_surface->role) {
|
||||
case WLR_XDG_SURFACE_ROLE_NONE:
|
||||
return NULL;
|
||||
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
|
||||
return surface;
|
||||
case WLR_XDG_SURFACE_ROLE_POPUP:
|
||||
surface = xdg_surface->popup->parent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (surface && wlr_surface_is_layer_surface(surface)) {
|
||||
return surface;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
request_cursor_notify(struct wl_listener *listener, void *data)
|
||||
{
|
||||
|
|
@ -227,6 +253,28 @@ input_inhibit_blocks_surface(struct seat *seat, struct wl_resource *resource)
|
|||
&& inhibiting_client != wl_resource_get_client(resource);
|
||||
}
|
||||
|
||||
static bool
|
||||
update_pressed_surface(struct seat *seat, struct view *view,
|
||||
struct wlr_scene_node *node, struct wlr_surface *surface)
|
||||
{
|
||||
/*
|
||||
* In most cases, we don't want to leave one surface and enter
|
||||
* another while a button is pressed. However, GTK/Wayland
|
||||
* menus (implemented as XDG popups) do need the leave/enter
|
||||
* events to work properly. To cover this case, we allow
|
||||
* leave/enter events between XDG popups and their toplevel.
|
||||
*/
|
||||
if (seat->pressed.surface && surface != seat->pressed.surface) {
|
||||
struct wlr_surface *toplevel = get_toplevel(surface);
|
||||
if (toplevel && toplevel == seat->pressed.toplevel) {
|
||||
seat_set_pressed(seat, view, node,
|
||||
surface, toplevel);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
process_cursor_motion_out_of_surface(struct server *server, uint32_t time)
|
||||
{
|
||||
|
|
@ -267,10 +315,10 @@ process_cursor_motion_out_of_surface(struct server *server, uint32_t time)
|
|||
* Common logic shared by cursor_update_focus() and process_cursor_motion()
|
||||
*/
|
||||
static void
|
||||
cursor_update_common(struct server *server, struct wlr_scene_node *node,
|
||||
struct wlr_surface *surface, double sx, double sy,
|
||||
enum ssd_part_type view_area, uint32_t time_msec,
|
||||
bool cursor_has_moved)
|
||||
cursor_update_common(struct server *server, struct view *view,
|
||||
struct wlr_scene_node *node, struct wlr_surface *surface,
|
||||
double sx, double sy, enum ssd_part_type view_area,
|
||||
uint32_t time_msec, bool cursor_has_moved)
|
||||
{
|
||||
struct seat *seat = &server->seat;
|
||||
struct wlr_seat *wlr_seat = seat->seat;
|
||||
|
|
@ -287,6 +335,7 @@ cursor_update_common(struct server *server, struct wlr_scene_node *node,
|
|||
|
||||
/* TODO: verify drag_icon logic */
|
||||
if (seat->pressed.surface && surface != seat->pressed.surface
|
||||
&& !update_pressed_surface(seat, view, node, surface)
|
||||
&& !seat->drag_icon) {
|
||||
if (cursor_has_moved) {
|
||||
/*
|
||||
|
|
@ -398,8 +447,8 @@ process_cursor_motion(struct server *server, uint32_t time)
|
|||
}
|
||||
}
|
||||
|
||||
cursor_update_common(server, node, surface, sx, sy, view_area, time,
|
||||
/*cursor_has_moved*/ true);
|
||||
cursor_update_common(server, view, node, surface, sx, sy, view_area,
|
||||
time, /*cursor_has_moved*/ true);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
|
|
@ -420,15 +469,15 @@ cursor_update_focus(struct server *server)
|
|||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
struct seat *seat = &server->seat;
|
||||
desktop_node_and_view_at(seat->server, seat->cursor->x,
|
||||
seat->cursor->y, &node, &sx, &sy, &view_area);
|
||||
struct view *view = desktop_node_and_view_at(seat->server,
|
||||
seat->cursor->x, seat->cursor->y, &node, &sx, &sy, &view_area);
|
||||
|
||||
if (is_surface(view_area)) {
|
||||
surface = lab_wlr_surface_from_node(node);
|
||||
}
|
||||
|
||||
/* Focus surface under cursor if it isn't already focused */
|
||||
cursor_update_common(server, node, surface, sx, sy, view_area,
|
||||
cursor_update_common(server, view, node, surface, sx, sy, view_area,
|
||||
msec(&now), /*cursor_has_moved*/ false);
|
||||
}
|
||||
|
||||
|
|
@ -798,8 +847,9 @@ cursor_button(struct wl_listener *listener, void *data)
|
|||
if (server->input_mode == LAB_INPUT_STATE_MENU) {
|
||||
if (close_menu) {
|
||||
menu_close_root(server);
|
||||
cursor_update_common(server, node, surface, sx, sy,
|
||||
view_area, event->time_msec, false);
|
||||
cursor_update_common(server, view, node,
|
||||
surface, sx, sy, view_area,
|
||||
event->time_msec, false);
|
||||
close_menu = false;
|
||||
}
|
||||
return;
|
||||
|
|
@ -814,9 +864,8 @@ cursor_button(struct wl_listener *listener, void *data)
|
|||
|
||||
/* Handle _press */
|
||||
if (surface) {
|
||||
seat->pressed.view = view;
|
||||
seat->pressed.node = node;
|
||||
seat->pressed.surface = surface;
|
||||
seat_set_pressed(seat, view, node, surface,
|
||||
get_toplevel(surface));
|
||||
}
|
||||
|
||||
if (server->input_mode == LAB_INPUT_STATE_MENU) {
|
||||
|
|
|
|||
|
|
@ -145,9 +145,6 @@ unmap(struct lab_layer_surface *layer)
|
|||
if (seat->focused_layer == layer->scene_layer_surface->layer_surface) {
|
||||
seat_set_focus_layer(seat, NULL);
|
||||
}
|
||||
if (seat->pressed.surface == layer->scene_layer_surface->layer_surface->surface) {
|
||||
seat_reset_pressed(seat);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
37
src/seat.c
37
src/seat.c
|
|
@ -370,10 +370,41 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pressed_surface_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wlr_surface *surface = data;
|
||||
struct seat *seat = wl_container_of(listener, seat,
|
||||
pressed_surface_destroy);
|
||||
|
||||
assert(surface == seat->pressed.surface);
|
||||
seat_reset_pressed(seat);
|
||||
}
|
||||
|
||||
void
|
||||
seat_set_pressed(struct seat *seat, struct view *view,
|
||||
struct wlr_scene_node *node, struct wlr_surface *surface,
|
||||
struct wlr_surface *toplevel)
|
||||
{
|
||||
assert(surface);
|
||||
seat_reset_pressed(seat);
|
||||
|
||||
seat->pressed.view = view;
|
||||
seat->pressed.node = node;
|
||||
seat->pressed.surface = surface;
|
||||
seat->pressed.toplevel = toplevel;
|
||||
seat->pressed_surface_destroy.notify = pressed_surface_destroy;
|
||||
wl_signal_add(&surface->events.destroy, &seat->pressed_surface_destroy);
|
||||
}
|
||||
|
||||
void
|
||||
seat_reset_pressed(struct seat *seat)
|
||||
{
|
||||
seat->pressed.view = NULL;
|
||||
seat->pressed.node = NULL;
|
||||
seat->pressed.surface = NULL;
|
||||
if (seat->pressed.surface) {
|
||||
seat->pressed.view = NULL;
|
||||
seat->pressed.node = NULL;
|
||||
seat->pressed.surface = NULL;
|
||||
seat->pressed.toplevel = NULL;
|
||||
wl_list_remove(&seat->pressed_surface_destroy.link);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -815,11 +815,6 @@ view_destroy(struct view *view)
|
|||
need_cursor_update = true;
|
||||
}
|
||||
|
||||
if (server->seat.pressed.view == view) {
|
||||
/* Mouse was pressed on surface and is still pressed */
|
||||
seat_reset_pressed(&server->seat);
|
||||
}
|
||||
|
||||
if (server->focused_view == view) {
|
||||
server->focused_view = NULL;
|
||||
need_cursor_update = true;
|
||||
|
|
|
|||
|
|
@ -10,13 +10,8 @@
|
|||
#include "labwc.h"
|
||||
#include "node.h"
|
||||
|
||||
struct view_child {
|
||||
struct wlr_surface *surface;
|
||||
struct view *parent;
|
||||
};
|
||||
|
||||
struct xdg_popup {
|
||||
struct view_child view_child;
|
||||
struct view *parent_view;
|
||||
struct wlr_xdg_popup *wlr_popup;
|
||||
|
||||
struct wl_listener destroy;
|
||||
|
|
@ -58,20 +53,27 @@ popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
|
|||
{
|
||||
struct xdg_popup *popup = wl_container_of(listener, popup, new_popup);
|
||||
struct wlr_xdg_popup *wlr_popup = data;
|
||||
xdg_popup_create(popup->view_child.parent, wlr_popup);
|
||||
xdg_popup_create(popup->parent_view, wlr_popup);
|
||||
}
|
||||
|
||||
void
|
||||
xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
|
||||
{
|
||||
struct wlr_xdg_surface *parent =
|
||||
wlr_surface_is_xdg_surface(wlr_popup->parent) ?
|
||||
wlr_xdg_surface_from_wlr_surface(wlr_popup->parent) : NULL;
|
||||
if (!parent) {
|
||||
wlr_log(WLR_ERROR, "parent is not a valid XDG surface");
|
||||
return;
|
||||
}
|
||||
|
||||
struct xdg_popup *popup = calloc(1, sizeof(struct xdg_popup));
|
||||
if (!popup) {
|
||||
return;
|
||||
}
|
||||
|
||||
popup->parent_view = view;
|
||||
popup->wlr_popup = wlr_popup;
|
||||
popup->view_child.parent = view;
|
||||
popup->view_child.surface = wlr_popup->base->surface;
|
||||
|
||||
popup->destroy.notify = handle_xdg_popup_destroy;
|
||||
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
|
||||
|
|
@ -85,12 +87,6 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
|
|||
* this, we always set the user data field of xdg_surfaces to the
|
||||
* corresponding scene node.
|
||||
*/
|
||||
if (!wlr_surface_is_xdg_surface(wlr_popup->parent)) {
|
||||
wlr_log(WLR_ERROR, "xdg_surface is not xdg");
|
||||
return;
|
||||
}
|
||||
struct wlr_xdg_surface *parent =
|
||||
wlr_xdg_surface_from_wlr_surface(wlr_popup->parent);
|
||||
struct wlr_scene_tree *parent_tree = parent->surface->data;
|
||||
wlr_popup->base->surface->data =
|
||||
wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base);
|
||||
|
|
|
|||
|
|
@ -101,9 +101,6 @@ unmanaged_handle_unmap(struct wl_listener *listener, void *data)
|
|||
* Mark the node as gone so a racing configure event
|
||||
* won't try to reposition the node while unmapped.
|
||||
*/
|
||||
if (unmanaged->node && seat->pressed.node == unmanaged->node) {
|
||||
seat_reset_pressed(seat);
|
||||
}
|
||||
unmanaged->node = NULL;
|
||||
cursor_update_focus(unmanaged->server);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue