diyac/xdg.c

213 lines
8.9 KiB
C
Raw Normal View History

2024-03-30 00:18:51 +01:00
#define _POSIX_C_SOURCE 200112L
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <assert.h>
#include <stdlib.h>
#include "xdg.h"
#include "cursor.h"
static void begin_interactive(struct diyac_toplevel *toplevel,
enum diyac_cursor_mode mode, uint32_t edges)
{
/* This function sets up an interactive move or resize operation, where the
* compositor stops propegating pointer events to clients and instead
* consumes them itself, to move or resize windows. */
struct diyac_server *server = toplevel->server;
struct wlr_surface *focused_surface =
server->seat.wlr_seat->pointer_state.focused_surface;
if (toplevel->xdg_toplevel->base->surface !=
wlr_surface_get_root_surface(focused_surface))
{
/* Deny move/resize requests from unfocused clients. */
return;
}
server->grabbed_toplevel = toplevel;
server->seat.cursor_mode = mode;
if (mode == DIYAC_CURSOR_MOVE)
{
server->grab_x = server->seat.cursor->x - toplevel->scene_tree->node.x;
server->grab_y = server->seat.cursor->y - toplevel->scene_tree->node.y;
}
else
{
struct wlr_box geo_box;
wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
double border_x = (toplevel->scene_tree->node.x + geo_box.x) +
((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
double border_y = (toplevel->scene_tree->node.y + geo_box.y) +
((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
server->grab_x = server->seat.cursor->x - border_x;
server->grab_y = server->seat.cursor->y - border_y;
server->grab_geobox = geo_box;
server->grab_geobox.x += toplevel->scene_tree->node.x;
server->grab_geobox.y += toplevel->scene_tree->node.y;
server->resize_edges = edges;
}
}
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_toplevel *toplevel = wl_container_of(listener, toplevel, map);
wl_list_insert(&toplevel->server->toplevels, &toplevel->link);
diyac_focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface);
}
static void xdg_toplevel_unmap(struct wl_listener *listener, void *data)
{
/* Called when the surface is unmapped, and should no longer be shown. */
struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, unmap);
/* Reset the cursor mode if the grabbed toplevel was unmapped. */
if (toplevel == toplevel->server->grabbed_toplevel)
{
diyac_reset_cursor_mode(toplevel->server);
}
wl_list_remove(&toplevel->link);
}
static void xdg_toplevel_request_move(
struct wl_listener *listener, void *data)
{
/* This event is raised when a client would like to begin an interactive
* move, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check the
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_move);
begin_interactive(toplevel, DIYAC_CURSOR_MOVE, 0);
}
static void xdg_toplevel_request_resize(
struct wl_listener *listener, void *data)
{
/* This event is raised when a client would like to begin an interactive
* resize, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check the
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
struct wlr_xdg_toplevel_resize_event *event = data;
struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize);
begin_interactive(toplevel, DIYAC_CURSOR_RESIZE, event->edges);
}
static void xdg_toplevel_request_maximize(
struct wl_listener *listener, void *data)
{
/* This event is raised when a client would like to maximize itself,
* typically because the user clicked on the maximize button on
* client-side decorations. diyac doesn't support maximization, but
* to conform to xdg-shell protocol we still must send a configure.
* wlr_xdg_surface_schedule_configure() is used to send an empty reply. */
struct diyac_toplevel *toplevel =
wl_container_of(listener, toplevel, request_maximize);
// wlr_wl_output* output = get_wl_output_from_surface(struct wlr_wl_backend *wl,
// struct wl_surface *surface);
wlr_log(WLR_ERROR, "request maximize %dx%d", toplevel->output->width, toplevel->output->height);
wlr_scene_node_set_position(&toplevel->scene_tree->node,0,0);
wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, toplevel->output->width, toplevel->output->height);
//wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base);
}
static void xdg_toplevel_request_fullscreen(
struct wl_listener *listener, void *data)
{
/* Just as with request_maximize, we must send a configure here. */
struct diyac_toplevel *toplevel =
wl_container_of(listener, toplevel, request_fullscreen);
wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base);
}
static void xdg_toplevel_destroy(struct wl_listener *listener, void *data)
{
/* Called when the xdg_toplevel is destroyed. */
struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, destroy);
wl_list_remove(&toplevel->map.link);
wl_list_remove(&toplevel->unmap.link);
wl_list_remove(&toplevel->destroy.link);
wl_list_remove(&toplevel->request_move.link);
wl_list_remove(&toplevel->request_resize.link);
wl_list_remove(&toplevel->request_maximize.link);
wl_list_remove(&toplevel->request_fullscreen.link);
free(toplevel);
}
void diyac_new_xdg_surface(struct wl_listener *listener, void *data)
{
/* This event is raised when wlr_xdg_shell receives a new xdg surface from a
* client, either a toplevel (application window) or popup. */
struct diyac_server *server =
wl_container_of(listener, server, new_xdg_surface);
struct wlr_xdg_surface *xdg_surface = data;
/* We must add xdg popups to the scene graph so they get rendered. The
* wlroots scene graph provides a helper for this, but to use it we must
* provide the proper parent scene node of the xdg popup. To enable this,
* we always set the user data field of xdg_surfaces to the corresponding
* scene node. */
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP)
{
struct wlr_xdg_surface *parent =
wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
assert(parent != NULL);
struct wlr_scene_tree *parent_tree = parent->data;
xdg_surface->data = wlr_scene_xdg_surface_create(
parent_tree, xdg_surface);
return;
}
assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
/* Allocate a diyac_toplevel for this surface */
struct diyac_toplevel *toplevel = calloc(1, sizeof(*toplevel));
toplevel->server = server;
toplevel->xdg_toplevel = xdg_surface->toplevel;
toplevel->scene_tree = wlr_scene_xdg_surface_create(
&toplevel->server->scene->tree, toplevel->xdg_toplevel->base);
toplevel->scene_tree->node.data = toplevel;
xdg_surface->data = toplevel->scene_tree;
struct wlr_output *output = wlr_output_layout_output_at(
server->output_layout, server->seat.cursor->x,
server->seat.cursor->y);
toplevel->output = output;
if (!output)
{
wlr_log(WLR_ERROR,
"No output available to assign layer surface");
}
/* Listen to the various events it can emit */
toplevel->map.notify = xdg_toplevel_map;
wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map);
toplevel->unmap.notify = xdg_toplevel_unmap;
wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap);
toplevel->destroy.notify = xdg_toplevel_destroy;
wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy);
/* cotd */
struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel;
toplevel->request_move.notify = xdg_toplevel_request_move;
wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move);
toplevel->request_resize.notify = xdg_toplevel_request_resize;
wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize);
toplevel->request_maximize.notify = xdg_toplevel_request_maximize;
wl_signal_add(&xdg_toplevel->events.request_maximize,
&toplevel->request_maximize);
toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
wl_signal_add(&xdg_toplevel->events.request_fullscreen,
&toplevel->request_fullscreen);
}