tRefactor window focus event handling - spkp - Stacking wayland compositor
 (HTM) git clone git://git.z3bra.org/spkp.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit 5367b98d9a0350d5a77dcedae2bc48f8e24bb6a5
 (DIR) parent 79437cc17ee546a1c4f0215ac2f377b6cd2d8e71
 (HTM) Author: Willy Goiffon <dev@z3bra.org>
       Date:   Mon,  9 Nov 2020 14:44:35 +0100
       
       Refactor window focus event handling
       
       - Pointer focus is set when the pointer enter a window
       - Keyboard focus is set when clicking inside a window
       
       Keyboard focus remains on a window until it is set somewhere else,
       while pointer focus is only set when the pointer is above the window.
       
       Diffstat:
         M compositor.c                        |     110 +++++++++++++++++++++++++++----
       
       1 file changed, 96 insertions(+), 14 deletions(-)
       ---
 (DIR) diff --git a/compositor.c b/compositor.c
       t@@ -127,7 +127,9 @@ static void cb_paint_cursor(struct wl_listener *, void *);
        static void add_keyboard(struct state *, struct wlr_input_device *);
        static void add_pointer(struct state *, struct wlr_input_device *);
        static void render(struct wlr_surface *, int, int, void *);
       -static int  keybinding(struct state *, uint32_t, uint32_t);
       +static void focus(struct window *, struct wlr_surface *);
       +static int keybinding(struct state *, uint32_t, uint32_t);
       +static struct window *underneath(struct state *, double, double);
        
        void
        usage(char *pgm)
       t@@ -303,6 +305,10 @@ cb_new_window(struct wl_listener *listener, void *data)
                window->server = server;
                window->surface = surface;
        
       +        window->x = 0;
       +        window->y = 0;
       +        window->mapped = 0;
       +
                window->map.notify = cb_map_window;
                window->unmap.notify = cb_unmap_window;
                window->destroy.notify = cb_destroy_window;
       t@@ -322,25 +328,13 @@ void
        cb_map_window(struct wl_listener *listener, void *data)
        {
                struct window *w;
       -        struct state *server;
       -        struct wlr_keyboard *kb;
        
                (void)data;
        
                w = wl_container_of(listener, w, map);
       -        server = w->server;
       -
                w->mapped = 1;
        
       -        /* give window keyboard focus */
       -        kb = wlr_seat_get_keyboard(server->seat);
       -        wlr_xdg_toplevel_set_activated(w->surface, true);
       -
       -        wlr_seat_keyboard_notify_enter(server->seat, w->surface->surface,
       -                kb->keycodes, kb->num_keycodes, &kb->modifiers);
       -
       -        wlr_seat_pointer_notify_enter(server->seat, w->surface->surface,
       -                server->cursor->x, server->cursor->y);
       +        focus(w, w->surface->surface);
        }
        
        /*
       t@@ -439,35 +433,63 @@ void cb_motion(struct wl_listener *listener, void *data)
        {
                struct state *server;
                struct wlr_event_pointer_motion *ev;
       +        struct window *w;
        
                server = wl_container_of(listener, server, motion);
                ev = data;
        
                wlr_cursor_move(server->cursor, ev->device, ev->delta_x, ev->delta_y);
                wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, "left_ptr", server->cursor);
       +
       +        /* activate underneath window, if any */
       +        if ((w = underneath(server, server->cursor->x, server->cursor->y))) {
       +                focus(w, w->surface->surface);
       +        } else {
       +                wlr_seat_pointer_clear_focus(server->seat);
       +        }
       +
        }
        
        void cb_motion_absolute(struct wl_listener *listener, void *data)
        {
                struct state *server;
                struct wlr_event_pointer_motion_absolute *ev;
       +        struct window *w;
        
                server = wl_container_of(listener, server, motion_absolute);
                ev = data;
        
                wlr_cursor_warp_absolute(server->cursor, ev->device, ev->x, ev->y);
                wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, "left_ptr", server->cursor);
       +
       +        if ((w = underneath(server, server->cursor->x, server->cursor->y))) {
       +                focus(w, w->surface->surface);
       +        } else {
       +                wlr_seat_pointer_clear_focus(server->seat);
       +        }
        }
        
        void cb_click(struct wl_listener *listener, void *data)
        {
                struct state *server;
                struct wlr_event_pointer_button *ev;
       +        struct window *w;
        
                server = wl_container_of(listener, server, click);
                ev = data;
        
                wlr_seat_pointer_notify_button(server->seat, ev->time_msec, ev->button, ev->state);
       +
       +        if ((w = underneath(server, server->cursor->x, server->cursor->y))) {
       +                /* reinsert window on top of the stack */
       +                wl_list_remove(&w->link);
       +                wl_list_insert(&server->windows, &w->link);
       +
       +                focus(w, w->surface->surface);
       +        } else {
       +                wlr_seat_pointer_clear_focus(server->seat);
       +        }
       +
        }
        
        void cb_scroll(struct wl_listener *listener, void *data)
       t@@ -566,6 +588,38 @@ render(struct wlr_surface *surface, int x, int y, void *data)
                wlr_surface_send_frame_done(surface, &rdata->when);
        }
        
       +/*
       + * Set keyboard focus on the given top-level window
       + */
       +void
       +focus(struct window *window, struct wlr_surface *surface)
       +{
       +        struct state *server;
       +        struct wlr_seat *seat;
       +        struct wlr_keyboard *kb;
       +        struct wlr_surface *focus;
       +        struct wlr_xdg_surface *toplevel;
       +
       +        server = window->server;
       +        seat = server->seat;
       +        kb = wlr_seat_get_keyboard(seat);
       +        focus = seat->keyboard_state.focused_surface;
       +
       +        if (focus == surface)
       +                return;
       +
       +        /* deactivate currently focused window */
       +        if (focus) {
       +                toplevel = wlr_xdg_surface_from_wlr_surface(focus);
       +                wlr_xdg_toplevel_set_activated(toplevel, false);
       +        }
       +
       +        /* keyboard + pointer focus */
       +        toplevel = window->surface;
       +        wlr_seat_keyboard_notify_enter(seat, toplevel->surface,
       +                kb->keycodes, kb->num_keycodes, &kb->modifiers);
       +}
       +
        int
        keybinding(struct state *server, uint32_t mod, uint32_t key)
        {
       t@@ -581,6 +635,34 @@ keybinding(struct state *server, uint32_t mod, uint32_t key)
                return 0;
        }
        
       +struct window *
       +underneath(struct state *server, double x, double y)
       +{
       +        double sx, sy;
       +        struct window *w;
       +        struct wlr_surface *surface;
       +
       +        wl_list_for_each(w, &server->windows, link) {
       +                /*
       +                 * retrieve the subsurface at the given coordinates.
       +                 * coordinates are given relatively to the top level
       +                 * window coordinates on the output. Subsurfaces can
       +                 * however exist outside the bounds of the top level
       +                 * window, and thus have negative coordinates for example
       +                 *
       +                 * sx, sy will be updated with the pointer coordinates
       +                 * within the subsurface (always positive).
       +                 */
       +                surface = wlr_xdg_surface_surface_at(w->surface,
       +                        x - w->x, y - w->y, &sx, &sy);
       +
       +                if (surface)
       +                        return w;
       +        }
       +
       +        return NULL;
       +}
       +
        int
        main(int argc, char *argv[])
        {