feat: add wlr_foreign_toplevel support

This commit is contained in:
DanyLE 2024-04-12 21:18:42 +02:00
parent bca498f387
commit dced6db8b2
9 changed files with 223 additions and 49 deletions

View File

@ -12,6 +12,7 @@ OBJS=\
seat.c \
node.c \
view.c \
foreign.c \
xdg.c \
xdg-shell-protocol.c \
layer.c \

View File

@ -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;

View File

@ -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. */

19
diyac.h
View File

@ -15,6 +15,7 @@
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#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

97
foreign.c Normal file
View File

@ -0,0 +1,97 @@
#include "foreign.h"
#include "view.h"
#include <wlr/util/log.h>
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);
}

6
foreign.h Normal file
View File

@ -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

93
view.c
View File

@ -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);
}

3
view.h
View File

@ -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

48
xdg.c
View File

@ -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);