From fb6f7d42a5df8e596868b2ac516b4f5e7cc7078a Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 11 Sep 2020 20:48:28 +0100 Subject: [PATCH] Add desktop.c Move from view.c functions which work on server->views --- include/labwc.h | 18 +++-- src/action.c | 3 +- src/cursor.c | 6 +- src/desktop.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++ src/keyboard.c | 6 +- src/meson.build | 1 + src/view.c | 206 ------------------------------------------------ src/xdg.c | 4 +- src/xwayland.c | 4 +- 9 files changed, 229 insertions(+), 222 deletions(-) create mode 100644 src/desktop.c diff --git a/include/labwc.h b/include/labwc.h index b30e45b6..ebc66987 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -169,6 +169,7 @@ void xwayland_unmanaged_create(struct server *server, struct wlr_xwayland_surface *xsurface); void view_init_position(struct view *view); + /** * view_get_surface_geometry - geometry relative to view * @view: toplevel containing the surface to process @@ -180,12 +181,19 @@ struct wlr_box view_geometry(struct view *view); void view_resize(struct view *view, struct wlr_box geo); void view_minimize(struct view *view); void view_unminimize(struct view *view); -void view_focus(struct view *view); -struct view *view_next(struct server *server, struct view *current); bool view_hasfocus(struct view *view); -struct view *view_at(struct server *server, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy, - int *view_area); + +void desktop_focus_view(struct view *view); + +/** + * desktop_next_view - return next view + * @current: view used as reference point for defining 'next' + * Note: If current==NULL, the list's second view is returned + */ +struct view *desktop_next_view(struct server *server, struct view *current); +struct view *desktop_view_at(struct server *server, double lx, double ly, + struct wlr_surface **surface, double *sx, + double *sy, int *view_area); void seat_init(struct wlr_seat *seat); void seat_focus_surface(struct wlr_surface *surface); diff --git a/src/action.c b/src/action.c index d2c403f7..1eff680d 100644 --- a/src/action.c +++ b/src/action.c @@ -11,7 +11,8 @@ void action(struct server *server, struct keybind *keybind) if (!strcasecmp(keybind->action, "Exit")) { wl_display_terminate(server->wl_display); } else if (!strcasecmp(keybind->action, "NextWindow")) { - server->cycle_view = view_next(server, server->cycle_view); + server->cycle_view = + desktop_next_view(server, server->cycle_view); } else if (!strcasecmp(keybind->action, "Execute")) { spawn_async_no_shell(keybind->command); } else if (!strcasecmp(keybind->action, "debug-views")) { diff --git a/src/cursor.c b/src/cursor.c index f2098a01..efcada27 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -73,7 +73,7 @@ static void process_cursor_motion(struct server *server, uint32_t time) struct wlr_seat *seat = server->seat; struct wlr_surface *surface = NULL; int view_area; - struct view *view = view_at(server, server->cursor->x, + struct view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy, &view_area); if (!view) { @@ -188,7 +188,7 @@ void cursor_button(struct wl_listener *listener, void *data) double sx, sy; struct wlr_surface *surface; int view_area; - struct view *view = view_at(server, server->cursor->x, + struct view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy, &view_area); if (event->state == WLR_BUTTON_RELEASED) { @@ -196,7 +196,7 @@ void cursor_button(struct wl_listener *listener, void *data) server->cursor_mode = LAB_CURSOR_PASSTHROUGH; } else { /* Focus that client if the button was _pressed_ */ - view_focus(view); + desktop_focus_view(view); switch (view_area) { case LAB_DECO_BUTTON_CLOSE: view->impl->close(view); diff --git a/src/desktop.c b/src/desktop.c new file mode 100644 index 00000000..82b9962e --- /dev/null +++ b/src/desktop.c @@ -0,0 +1,203 @@ +#include "labwc.h" +#include "common/bug-on.h" + +static void move_to_front(struct view *view) +{ + wl_list_remove(&view->link); + wl_list_insert(&view->server->views, &view->link); +} + +static struct wlr_xwayland_surface *top_parent_of(struct view *view) +{ + struct wlr_xwayland_surface *s = view->xwayland_surface; + while (s->parent) + s = s->parent; + return s; +} + +static void move_xwayland_sub_views_to_front(struct view *parent) +{ + if (!parent || parent->type != LAB_XWAYLAND_VIEW) + return; + struct view *view, *next; + wl_list_for_each_reverse_safe(view, next, &parent->server->views, link) + { + /* need to stop here, otherwise loops keeps going forever */ + if (view == parent) + break; + if (view->type != LAB_XWAYLAND_VIEW) + continue; + if (!view->mapped && !view->minimized) + continue; + if (top_parent_of(view) != parent->xwayland_surface) + continue; + move_to_front(view); + /* TODO: we should probably focus on these too here */ + } +} + +/* Activate/deactivate toplevel surface */ +static void set_activated(struct wlr_surface *surface, bool activated) +{ + if (!surface) + return; + if (wlr_surface_is_xdg_surface(surface)) { + struct wlr_xdg_surface *previous; + previous = wlr_xdg_surface_from_wlr_surface(surface); + wlr_xdg_toplevel_set_activated(previous, activated); + } else { + struct wlr_xwayland_surface *previous; + previous = wlr_xwayland_surface_from_wlr_surface(surface); + wlr_xwayland_surface_activate(previous, activated); + } +} + +void desktop_focus_view(struct view *view) +{ + /* Note: this function only deals with keyboard focus. */ + if (!view) + return; + + /* TODO: messy - sort out */ + if (!view->mapped && view->minimized) { + view_unminimize(view); + return; + } + + struct server *server = view->server; + struct wlr_seat *seat = server->seat; + struct wlr_surface *prev_surface; + prev_surface = seat->keyboard_state.focused_surface; + if (prev_surface == view->surface) { + /* Don't re-focus an already focused surface. */ + return; + } + if (view->type == LAB_XWAYLAND_VIEW) { + /* Don't focus on menus, etc */ + move_to_front(view); + if (!wlr_xwayland_or_surface_wants_focus( + view->xwayland_surface)) { + return; + } + } + if (prev_surface) + set_activated(seat->keyboard_state.focused_surface, false); + + move_to_front(view); + set_activated(view->surface, true); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + wlr_seat_keyboard_notify_enter(seat, view->surface, keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers); + + move_xwayland_sub_views_to_front(view); +} + +/* + * Some xwayland apps produce unmapped surfaces on startup and also leave + * some unmapped surfaces kicking around on 'close' (for example * leafpad's + * "about" dialogue). Whilst this is not normally a problem, we have to be + * careful when cycling between views. The only view's we should focus are + * those that are already mapped and those that have been minimized. + */ +static bool isfocusable(struct view *view) +{ + /* filter out those xwayland surfaces that have never been mapped */ + if (!view->surface) + return false; + return (view->mapped || view->minimized); +} + +static int has_focusable_view(struct wl_list *wl_list) +{ + struct view *view; + wl_list_for_each (view, wl_list, link) { + if (isfocusable(view)) + return true; + } + return false; +} + +static struct view *first_view(struct server *server) +{ + struct view *view; + view = wl_container_of(server->views.next, view, link); + return view; +} + +struct view *desktop_next_view(struct server *server, struct view *current) +{ + if (!has_focusable_view(&server->views)) + return NULL; + + struct view *view = current ? current : first_view(server); + + /* Replacement for wl_list_for_each_from() */ + do { + view = wl_container_of(view->link.next, view, link); + } while (&view->link == &server->views || !isfocusable(view)); + return view; +} + +static bool _view_at(struct view *view, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) +{ + /* + * XDG toplevels may have nested surfaces, such as popup windows for + * context menus or tooltips. This function tests if any of those are + * underneath the coordinates lx and ly (in output Layout Coordinates). + * If so, it sets the surface pointer to that wlr_surface and the sx and + * sy coordinates to the coordinates relative to that surface's top-left + * corner. + */ + double view_sx = lx - view->x; + double view_sy = ly - view->y; + double _sx, _sy; + struct wlr_surface *_surface = NULL; + + switch (view->type) { + case LAB_XDG_SHELL_VIEW: + _surface = wlr_xdg_surface_surface_at( + view->xdg_surface, view_sx, view_sy, &_sx, &_sy); + break; + case LAB_XWAYLAND_VIEW: + if (!view->xwayland_surface->surface) + return false; + _surface = + wlr_surface_surface_at(view->xwayland_surface->surface, + view_sx, view_sy, &_sx, &_sy); + break; + } + + if (_surface) { + *sx = _sx; + *sy = _sy; + *surface = _surface; + return true; + } + return false; +} + +struct view *desktop_view_at(struct server *server, double lx, double ly, + struct wlr_surface **surface, double *sx, + double *sy, int *view_area) +{ + /* + * This iterates over all of our surfaces and attempts to find one under + * the cursor. It relies on server->views being ordered from + * top-to-bottom. + */ + struct view *view; + wl_list_for_each (view, &server->views, link) { + if (!view->mapped) + continue; + if (_view_at(view, lx, ly, surface, sx, sy)) + return view; + if (!view->show_server_side_deco) + continue; + *view_area = deco_at(view, lx, ly); + if (*view_area != LAB_DECO_NONE) + return view; + } + return NULL; +} diff --git a/src/keyboard.c b/src/keyboard.c index 75b2ac08..08e2372b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -61,12 +61,12 @@ static void keyboard_handle_key(struct wl_listener *listener, void *data) if ((syms[0] == XKB_KEY_Alt_L) && event->state == WLR_KEY_RELEASED) { /* end cycle */ - view_focus(server->cycle_view); + desktop_focus_view(server->cycle_view); server->cycle_view = NULL; } else if (event->state == WLR_KEY_PRESSED) { /* cycle to next */ - server->cycle_view = view_next(server, - server->cycle_view); + server->cycle_view = + desktop_next_view(server, server->cycle_view); return; } } diff --git a/src/meson.build b/src/meson.build index fd4635bb..7bc14dd6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -3,6 +3,7 @@ labwc_sources = files( 'cursor.c', 'dbg.c', 'deco.c', + 'desktop.c', 'interactive.c', 'keyboard.c', 'main.c', diff --git a/src/view.c b/src/view.c index 71226046..e28588c0 100644 --- a/src/view.c +++ b/src/view.c @@ -85,28 +85,6 @@ void view_unminimize(struct view *view) view->impl->map(view); } -static void move_to_front(struct view *view) -{ - wl_list_remove(&view->link); - wl_list_insert(&view->server->views, &view->link); -} - -/* Activate/deactivate toplevel surface */ -static void set_activated(struct wlr_surface *surface, bool activated) -{ - if (!surface) - return; - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *previous; - previous = wlr_xdg_surface_from_wlr_surface(surface); - wlr_xdg_toplevel_set_activated(previous, activated); - } else { - struct wlr_xwayland_surface *previous; - previous = wlr_xwayland_surface_from_wlr_surface(surface); - wlr_xwayland_surface_activate(previous, activated); - } -} - bool view_hasfocus(struct view *view) { if (!view || !view->surface) @@ -114,187 +92,3 @@ bool view_hasfocus(struct view *view) struct wlr_seat *seat = view->server->seat; return (view->surface == seat->keyboard_state.focused_surface); } - -static struct wlr_xwayland_surface *top_parent_of(struct view *view) -{ - struct wlr_xwayland_surface *s = view->xwayland_surface; - while (s->parent) - s = s->parent; - return s; -} - -static void move_xwayland_sub_views_to_front(struct view *parent) -{ - if (!parent || parent->type != LAB_XWAYLAND_VIEW) - return; - struct view *view, *next; - wl_list_for_each_reverse_safe(view, next, &parent->server->views, link) - { - /* need to stop here, otherwise loops keeps going forever */ - if (view == parent) - break; - if (view->type != LAB_XWAYLAND_VIEW) - continue; - if (!view->mapped && !view->minimized) - continue; - if (top_parent_of(view) != parent->xwayland_surface) - continue; - move_to_front(view); - /* TODO: we should probably focus on these too here */ - } -} - -void view_focus(struct view *view) -{ - /* Note: this function only deals with keyboard focus. */ - if (!view) - return; - - /* TODO: messy - sort out */ - if (!view->mapped && view->minimized) { - view_unminimize(view); - return; - } - - struct server *server = view->server; - struct wlr_seat *seat = server->seat; - struct wlr_surface *prev_surface; - prev_surface = seat->keyboard_state.focused_surface; - if (prev_surface == view->surface) { - /* Don't re-focus an already focused surface. */ - return; - } - if (view->type == LAB_XWAYLAND_VIEW) { - /* Don't focus on menus, etc */ - move_to_front(view); - if (!wlr_xwayland_or_surface_wants_focus( - view->xwayland_surface)) { - return; - } - } - if (prev_surface) - set_activated(seat->keyboard_state.focused_surface, false); - - move_to_front(view); - set_activated(view->surface, true); - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); - wlr_seat_keyboard_notify_enter(seat, view->surface, keyboard->keycodes, - keyboard->num_keycodes, - &keyboard->modifiers); - - move_xwayland_sub_views_to_front(view); -} - -static struct view *first_view(struct server *server) -{ - struct view *view; - view = wl_container_of(server->views.next, view, link); - return view; -} - -/* - * Some xwayland apps produce unmapped surfaces on startup and also leave - * some unmapped surfaces kicking around on 'close' (for example * leafpad's - * "about" dialogue). Whilst this is not normally a problem, we have to be - * careful when cycling between views. The only view's we should focus are - * those that are already mapped and those that have been minimized. - */ -static bool isfocusable(struct view *view) -{ - /* filter out those xwayland surfaces that have never been mapped */ - if (!view->surface) - return false; - return (view->mapped || view->minimized); -} - -static int has_focusable_view(struct wl_list *wl_list) -{ - struct view *view; - wl_list_for_each (view, wl_list, link) { - if (isfocusable(view)) - return true; - } - return false; -} - -/** - * view_next - return next view - * @current: view used as reference point for defining 'next' - * Note: If @current=NULL, the list's second view is returned - */ -struct view *view_next(struct server *server, struct view *current) -{ - if (!has_focusable_view(&server->views)) - return NULL; - - struct view *view = current ? current : first_view(server); - - /* Replacement for wl_list_for_each_from() */ - do { - view = wl_container_of(view->link.next, view, link); - } while (&view->link == &server->views || !isfocusable(view)); - return view; -} - -static bool _view_at(struct view *view, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) -{ - /* - * XDG toplevels may have nested surfaces, such as popup windows for - * context menus or tooltips. This function tests if any of those are - * underneath the coordinates lx and ly (in output Layout Coordinates). - * If so, it sets the surface pointer to that wlr_surface and the sx and - * sy coordinates to the coordinates relative to that surface's top-left - * corner. - */ - double view_sx = lx - view->x; - double view_sy = ly - view->y; - double _sx, _sy; - struct wlr_surface *_surface = NULL; - - switch (view->type) { - case LAB_XDG_SHELL_VIEW: - _surface = wlr_xdg_surface_surface_at( - view->xdg_surface, view_sx, view_sy, &_sx, &_sy); - break; - case LAB_XWAYLAND_VIEW: - if (!view->xwayland_surface->surface) - return false; - _surface = - wlr_surface_surface_at(view->xwayland_surface->surface, - view_sx, view_sy, &_sx, &_sy); - break; - } - - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return true; - } - return false; -} - -struct view *view_at(struct server *server, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy, - int *view_area) -{ - /* - * This iterates over all of our surfaces and attempts to find one under - * the cursor. It relies on server->views being ordered from - * top-to-bottom. - */ - struct view *view; - wl_list_for_each (view, &server->views, link) { - if (!view->mapped) - continue; - if (_view_at(view, lx, ly, surface, sx, sy)) - return view; - if (!view->show_server_side_deco) - continue; - *view_area = deco_at(view, lx, ly); - if (*view_area != LAB_DECO_NONE) - return view; - } - return NULL; -} diff --git a/src/xdg.c b/src/xdg.c index 5d48560c..df9c71d9 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -141,14 +141,14 @@ static void xdg_toplevel_view_map(struct view *view) wl_signal_add(&view->xdg_surface->surface->events.commit, &view->commit); view->commit.notify = handle_commit; - view_focus(view); + desktop_focus_view(view); } static void xdg_toplevel_view_unmap(struct view *view) { view->mapped = false; wl_list_remove(&view->commit.link); - view_focus(view_next(view->server, view)); + desktop_focus_view(desktop_next_view(view->server, view)); } static const struct view_impl xdg_toplevel_view_impl = { diff --git a/src/xwayland.c b/src/xwayland.c index f0a9df86..ae10e26d 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -80,14 +80,14 @@ static void map(struct view *view) &view->commit); view->commit.notify = handle_commit; - view_focus(view); + desktop_focus_view(view); } static void unmap(struct view *view) { view->mapped = false; wl_list_remove(&view->commit.link); - view_focus(view_next(view->server, view)); + desktop_focus_view(desktop_next_view(view->server, view)); } static const struct view_impl xwl_view_impl = {