From dced6db8b25dfe303522a912b047c0dd60443ac4 Mon Sep 17 00:00:00 2001 From: DanyLE Date: Fri, 12 Apr 2024 21:18:42 +0200 Subject: [PATCH] feat: add wlr_foreign_toplevel support --- Makefile | 1 + cursor.c | 3 ++ diyac.c | 2 ++ diyac.h | 19 +++++++++-- foreign.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ foreign.h | 6 ++++ view.c | 93 ++++++++++++++++++++++++++++++++++++++-------------- view.h | 3 ++ xdg.c | 48 ++++++++++++++------------- 9 files changed, 223 insertions(+), 49 deletions(-) create mode 100644 foreign.c create mode 100644 foreign.h diff --git a/Makefile b/Makefile index 6d3eddc..3819676 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ OBJS=\ seat.c \ node.c \ view.c \ + foreign.c \ xdg.c \ xdg-shell-protocol.c \ layer.c \ diff --git a/cursor.c b/cursor.c index e811134..9b4477a 100644 --- a/cursor.c +++ b/cursor.c @@ -22,6 +22,8 @@ void diyac_cursor_focus(struct diyac_server *server) switch (desc->type) { case DIYAC_NODE_VIEW: + diyac_focus_view(desc->data, true); + /* root = diyac_get_root_view(desc->data); if (root) { @@ -31,6 +33,7 @@ void diyac_cursor_focus(struct diyac_server *server) { diyac_focus_view(desc->data, true); } + */ break; case DIYAC_NODE_LAYER_SURFACE: layer = desc->data; diff --git a/diyac.c b/diyac.c index a1494b5..8c9ebc0 100644 --- a/diyac.c +++ b/diyac.c @@ -142,6 +142,8 @@ int main(int argc, char *argv[]) * let us know when new input devices are available on the backend. */ wl_list_init(&server.seat.keyboards); + /* foreign toplevel manager for create shell panel for application control */ + server.foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server.wl_display); diyac_init_seat(&server); /* Add a Unix socket to the Wayland display. */ diff --git a/diyac.h b/diyac.h index 2576f46..8db5f7e 100644 --- a/diyac.h +++ b/diyac.h @@ -15,6 +15,7 @@ #include #include #include +#include #define LAYER_TREE_SZ 4 /* For brevity's sake, struct members are annotated where they are used. */ @@ -43,9 +44,9 @@ struct diyac_view_state bool fullscreen; bool minimized; /** - * if neither state is on, it means that + * if neither state is on, it means that * the current state is normal - */ + */ }; struct diyac_seat @@ -113,6 +114,8 @@ struct diyac_server struct wl_listener new_xdg_surface; struct wl_listener new_layer_surface; struct wl_list views; + struct diyac_view * active_view; + struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; /* * Popups need to be rendered above always-on-top views, so we reparent @@ -162,6 +165,17 @@ struct diyac_output struct wlr_scene_tree *layer_tree[LAYER_TREE_SZ]; }; +struct foreign_toplevel +{ + struct wlr_foreign_toplevel_handle_v1 *handle; + struct wl_listener maximize; + struct wl_listener minimize; + struct wl_listener fullscreen; + struct wl_listener activate; + struct wl_listener close; + struct wl_listener destroy; +}; + struct diyac_view { struct wl_list link; @@ -191,6 +205,7 @@ struct diyac_view struct wl_listener set_app_id; // only useful when using SSD struct wl_listener new_popup; + struct foreign_toplevel toplevel; }; struct diyac_keyboard diff --git a/foreign.c b/foreign.c new file mode 100644 index 0000000..f03e8d9 --- /dev/null +++ b/foreign.c @@ -0,0 +1,97 @@ +#include "foreign.h" +#include "view.h" +#include + +static void handle_request_minimize(struct wl_listener *listener, void *data) +{ + struct diyac_view *view = wl_container_of(listener, view, toplevel.minimize); + struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; + // view_minimize(view, event->minimized); + diyac_view_set_mimimize(view, event->minimized); + wlr_log(WLR_INFO, "foreign: request minimize"); + +} + +static void handle_request_maximize(struct wl_listener *listener, void *data) +{ + struct diyac_view *view = wl_container_of(listener, view, toplevel.maximize); + struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; + // view_maximize(view, event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE, + // /*store_natural_geometry*/ true); + diyac_view_set_maximize(view, event->maximized); + wlr_log(WLR_INFO, "foreign: request maximize"); +} + +static void handle_request_fullscreen(struct wl_listener *listener, void *data) +{ + struct diyac_view *view = wl_container_of(listener, view, toplevel.fullscreen); + struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; + // view_set_fullscreen(view, event->fullscreen); + diyac_view_set_fullscreen(view, event->fullscreen); + wlr_log(WLR_INFO, "foreign: request fullscreen"); +} + +static void handle_request_activate(struct wl_listener *listener, void *data) +{ + struct diyac_view *view = wl_container_of(listener, view, toplevel.activate); + // struct wlr_foreign_toplevel_handle_v1_activated_event *event = data; + /* In a multi-seat world we would select seat based on event->seat here. */ + // desktop_focus_view(view, /*raise*/ true); + diyac_focus_view(view,true); + wlr_log(WLR_INFO, "foreign: request activate"); +} + +static void +handle_request_close(struct wl_listener *listener, void *data) +{ + struct diyac_view *view = wl_container_of(listener, view, toplevel.close); + //view_close(view); +} + +static void +handle_destroy(struct wl_listener *listener, void *data) +{ + struct diyac_view *view = wl_container_of(listener, view, toplevel.destroy); + struct foreign_toplevel *toplevel = &view->toplevel; + wl_list_remove(&toplevel->maximize.link); + wl_list_remove(&toplevel->minimize.link); + wl_list_remove(&toplevel->fullscreen.link); + wl_list_remove(&toplevel->activate.link); + wl_list_remove(&toplevel->close.link); + wl_list_remove(&toplevel->destroy.link); + toplevel->handle = NULL; +} + +void diyac_init_foreign_toplevel(struct diyac_view *view) +{ + struct foreign_toplevel *toplevel = &view->toplevel; + toplevel->handle = wlr_foreign_toplevel_handle_v1_create( + view->server->foreign_toplevel_manager); + if (!toplevel->handle) + { + wlr_log(WLR_ERROR, "cannot create foreign toplevel handle"); + return; + } + toplevel->maximize.notify = handle_request_maximize; + wl_signal_add(&toplevel->handle->events.request_maximize, + &toplevel->maximize); + + toplevel->minimize.notify = handle_request_minimize; + wl_signal_add(&toplevel->handle->events.request_minimize, + &toplevel->minimize); + + toplevel->fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&toplevel->handle->events.request_fullscreen, + &toplevel->fullscreen); + + toplevel->activate.notify = handle_request_activate; + wl_signal_add(&toplevel->handle->events.request_activate, + &toplevel->activate); + + toplevel->close.notify = handle_request_close; + wl_signal_add(&toplevel->handle->events.request_close, + &toplevel->close); + + toplevel->destroy.notify = handle_destroy; + wl_signal_add(&toplevel->handle->events.destroy, &toplevel->destroy); +} \ No newline at end of file diff --git a/foreign.h b/foreign.h new file mode 100644 index 0000000..07226ee --- /dev/null +++ b/foreign.h @@ -0,0 +1,6 @@ +#ifndef DIYAC_FOREIGN_H +#define DIYAC_FOREIGN_H +#include "diyac.h" + +void diyac_init_foreign_toplevel(struct diyac_view* view); +#endif \ No newline at end of file diff --git a/view.c b/view.c index 08b82f5..9aa812d 100644 --- a/view.c +++ b/view.c @@ -5,6 +5,25 @@ #include "node.h" #include "seat.h" #include "output.h" +#include "cursor.h" + +static void diyac_view_set_activated(struct diyac_view * view, bool activated) +{ + struct diyac_server *server = view->server; + wlr_xdg_toplevel_set_activated(view->xdg_toplevel, activated); + if(view->toplevel.handle) + { + wlr_foreign_toplevel_handle_v1_set_activated(view->toplevel.handle, activated); + } + server->active_view = NULL; + if(activated) + { + + diyac_seat_focus_surface(&server->seat, view->xdg_toplevel->base->surface); + server->active_view = view; + } +} + static void raise_to_front(struct diyac_view *view) { /* Move the toplevel to the front */ @@ -12,8 +31,8 @@ static void raise_to_front(struct diyac_view *view) wl_list_remove(&view->link); wl_list_insert(&view->server->views, &view->link); /* Activate the new surface */ - wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true); } + void diyac_focus_view(struct diyac_view *toplevel, bool raise) { /* Note: this function only deals with keyboard focus. */ @@ -26,27 +45,15 @@ void diyac_focus_view(struct diyac_view *toplevel, bool raise) // dont focus unmapped view return; } - struct diyac_server *server = toplevel->server; - struct wlr_surface *prev_surface = server->seat.wlr_seat->keyboard_state.focused_surface; - if (prev_surface == toplevel->xdg_toplevel->base->surface) + if(toplevel == toplevel->server->active_view) { // Don't re-focus an already focused surface. wlr_log(WLR_DEBUG, "Don't re-focus an already focused surface"); return; } - if (prev_surface) + if(toplevel->server->active_view) { - /* - * Deactivate the previously focused surface. This lets the client know - * it no longer has focus and the client will repaint accordingly, e.g. - * stop displaying a caret. - */ - struct wlr_xdg_toplevel *prev_toplevel = - wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); - if (prev_toplevel != NULL) - { - wlr_xdg_toplevel_set_activated(prev_toplevel, false); - } + diyac_view_set_activated(toplevel->server->active_view, false); } raise_to_front(toplevel); if (raise) @@ -61,13 +68,12 @@ void diyac_focus_view(struct diyac_view *toplevel, bool raise) } wl_array_release(&subviews); } - - /* - * Tell the seat to have the keyboard enter this surface. wlroots will keep - * track of this and automatically send key events to the appropriate - * clients without additional work on your part. - */ - diyac_seat_focus_surface(&server->seat, toplevel->xdg_toplevel->base->surface); + diyac_view_set_activated(toplevel, true); + //diyac_seat_focus_surface(&server->seat, toplevel->xdg_toplevel->base->surface); + /*if(toplevel->toplevel.handle) + { + wlr_foreign_toplevel_handle_v1_set_activated(toplevel->toplevel.handle, true); + }*/ } struct diyac_view *diyac_view_at( @@ -100,6 +106,7 @@ void diyac_focus_topmost_view(struct diyac_server *server, bool raise) } else { + wlr_log(WLR_INFO, "No view found"); /* * Defocus previous focused surface/view if no longer * focusable (e.g. unmapped or on a different workspace). @@ -124,7 +131,6 @@ struct diyac_view *diyac_topmost_focusable_view(struct diyac_server *server) continue; } view = diyac_view_from_node(node); - return view; if (view->mapped /*&& view_is_focusable_from(view, prev)*/) { @@ -181,12 +187,20 @@ bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed) if(view->state.fullscreen && !view->requested.fullscreen) { wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, false); + if(view->toplevel.handle) + { + wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel.handle, false); + } wlr_scene_node_set_enabled(&view->output->scenes.top->node, true); view->state.fullscreen = false; } if(view->state.maximized && !view->requested.maximized) { wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, false); + if(view->toplevel.handle) + { + wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel.handle, false); + } view->state.maximized = false; } view->state = view->requested; @@ -202,6 +216,10 @@ bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed) wlr_scene_node_set_position(&view->scene_tree->node, 0, 0); wlr_xdg_toplevel_set_size(view->xdg_toplevel, view->output->wlr_output->width, view->output->wlr_output->height); wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, false); + if(view->toplevel.handle) + { + wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel.handle, true); + } return true; } else if(view->requested.maximized) @@ -209,6 +227,10 @@ bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed) wlr_scene_node_set_position(&view->scene_tree->node, usable.x, usable.y); wlr_xdg_toplevel_set_size(view->xdg_toplevel, usable.width, usable.height); wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, true); + if(view->toplevel.handle) + { + wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel.handle, true); + } return true; } else @@ -304,4 +326,27 @@ struct wlr_box diyac_view_get_geometry(struct diyac_view *view) return view->output->usable_area; } return view->original; +} + +void diyac_view_set_maximize(struct diyac_view * view, bool activated) +{ + view->requested.maximized = activated; + diyac_reset_cursor_mode(view->server); + diyac_view_update_geometry(view, false); +} +void diyac_view_set_fullscreen(struct diyac_view * view, bool activated) +{ + view->requested.fullscreen = activated; + /** + * TODO: use client specific output for fullscreen + * toplevel->xdg_toplevel->requested.fullscreen_output + */ + diyac_reset_cursor_mode(view->server); + diyac_view_update_geometry(view, false); +} +void diyac_view_set_mimimize(struct diyac_view * view, bool activated) +{ + // view->requested.minimized = activated; + //TODO implement minimize + wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); } \ No newline at end of file diff --git a/view.h b/view.h index 4aafa01..f620846 100644 --- a/view.h +++ b/view.h @@ -15,4 +15,7 @@ void diyac_arrange_all_views(struct diyac_server *server); struct diyac_view *diyac_get_root_view(struct diyac_view *view); void diyac_get_children_views(struct diyac_view *view, struct wl_array *children); struct wlr_box diyac_view_get_geometry(struct diyac_view *view); +void diyac_view_set_maximize(struct diyac_view * view, bool activated); +void diyac_view_set_fullscreen(struct diyac_view * view, bool activated); +void diyac_view_set_mimimize(struct diyac_view * view, bool activated); #endif \ No newline at end of file diff --git a/xdg.c b/xdg.c index e43bbc5..e45eff3 100644 --- a/xdg.c +++ b/xdg.c @@ -7,6 +7,7 @@ #include "node.h" #include "view.h" #include "output.h" +#include "foreign.h" static void xdg_popup_create(struct diyac_view *view, struct wlr_xdg_popup *wlr_popup); @@ -58,6 +59,10 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) /* Called when the surface is mapped, or ready to display on-screen. */ struct diyac_view *toplevel = wl_container_of(listener, toplevel, map); + if(toplevel->mapped) + { + return; + } /* wlr_xdg_toplevel_set_wm_capabilities(toplevel->xdg_toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | @@ -67,6 +72,7 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &toplevel->original); wlr_scene_node_set_enabled(&toplevel->scene_tree->node, true); toplevel->mapped = true; + diyac_init_foreign_toplevel(toplevel); wl_list_insert(&toplevel->server->views, &toplevel->link); toplevel->original.x = (toplevel->output->usable_area.width - toplevel->original.width) / 2; toplevel->original.y = (toplevel->output->usable_area.height - toplevel->original.height) / 2; @@ -100,6 +106,22 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) */ wlr_scene_node_set_enabled(&toplevel->output->scenes.top->node, true); } + if(toplevel->server->active_view == toplevel) + { + toplevel->server->active_view = NULL; + } + struct diyac_view *root = diyac_get_root_view(toplevel); + if (root && root->mapped) + { + diyac_focus_view(root, true); + //wlr_log(WLR_INFO, "focus root"); + } + else + { + //wlr_log(WLR_INFO, "focus topmost"); + diyac_focus_topmost_view(toplevel->server, true); + } + wl_list_remove(&toplevel->link); } @@ -140,31 +162,21 @@ static void xdg_toplevel_request_maximize( struct diyac_view *toplevel = wl_container_of(listener, toplevel, request_maximize); - toplevel->requested.maximized = toplevel->xdg_toplevel->requested.maximized; - diyac_reset_cursor_mode(toplevel->server); - diyac_view_update_geometry(toplevel, false); + diyac_view_set_maximize(toplevel, toplevel->xdg_toplevel->requested.maximized); } static void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data) { struct diyac_view *toplevel = wl_container_of(listener, toplevel, request_fullscreen); - toplevel->requested.fullscreen = toplevel->xdg_toplevel->requested.fullscreen; - /** - * TODO: use client specific output for fullscreen - * toplevel->xdg_toplevel->requested.fullscreen_output - */ - diyac_reset_cursor_mode(toplevel->server); - diyac_view_update_geometry(toplevel, false); + diyac_view_set_fullscreen(toplevel,toplevel->xdg_toplevel->requested.fullscreen); } static void xdg_toplevel_request_minimize(struct wl_listener *listener, void *data) { struct diyac_view *toplevel = wl_container_of(listener, toplevel, request_minimize); - toplevel->requested.minimized = toplevel->xdg_toplevel->requested.minimized; - //TODO implement minimize - wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + diyac_view_set_mimimize(toplevel, toplevel->xdg_toplevel->requested.minimized); } static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) @@ -172,16 +184,6 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) /* Called when the xdg_toplevel is destroyed. */ struct diyac_view *toplevel = wl_container_of(listener, toplevel, destroy); - struct diyac_view *root = diyac_get_root_view(toplevel); - if (root && root->mapped) - { - diyac_focus_view(root, true); - } - else - { - diyac_focus_topmost_view(toplevel->server, true); - } - wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); wl_list_remove(&toplevel->destroy.link);