mirror of
https://github.com/labwc/labwc.git
synced 2026-02-15 22:05:25 -05:00
These were added to fix handling of natural geometry for snap-to-edge
behavior back in 9021020f6e and seemed like a good idea at the time.
Since then, the number of call sites has exploded, so it seems more
maintainable to put explicit checks for interactive move within the
three functions affected.
250 lines
8.2 KiB
C
250 lines
8.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
#include "foreign-toplevel/wlr-foreign.h"
|
|
#include <assert.h>
|
|
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
|
|
#include "common/macros.h"
|
|
#include "labwc.h"
|
|
#include "output.h"
|
|
#include "view.h"
|
|
|
|
/* wlr signals */
|
|
static void
|
|
handle_request_minimize(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on.request_minimize);
|
|
struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data;
|
|
|
|
view_minimize(wlr_toplevel->view, event->minimized);
|
|
}
|
|
|
|
static void
|
|
handle_request_maximize(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on.request_maximize);
|
|
struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
|
|
|
|
view_maximize(wlr_toplevel->view,
|
|
event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE);
|
|
}
|
|
|
|
static void
|
|
handle_request_fullscreen(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on.request_fullscreen);
|
|
struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
|
|
|
|
/* TODO: This ignores event->output */
|
|
view_set_fullscreen(wlr_toplevel->view, event->fullscreen);
|
|
}
|
|
|
|
static void
|
|
handle_request_activate(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on.request_activate);
|
|
|
|
/* In a multi-seat world we would select seat based on event->seat here. */
|
|
desktop_focus_view(wlr_toplevel->view, /*raise*/ true);
|
|
}
|
|
|
|
static void
|
|
handle_request_close(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on.request_close);
|
|
|
|
view_close(wlr_toplevel->view);
|
|
}
|
|
|
|
static void
|
|
handle_handle_destroy(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on.handle_destroy);
|
|
|
|
/* Client side requests */
|
|
wl_list_remove(&wlr_toplevel->on.request_maximize.link);
|
|
wl_list_remove(&wlr_toplevel->on.request_minimize.link);
|
|
wl_list_remove(&wlr_toplevel->on.request_fullscreen.link);
|
|
wl_list_remove(&wlr_toplevel->on.request_activate.link);
|
|
wl_list_remove(&wlr_toplevel->on.request_close.link);
|
|
wl_list_remove(&wlr_toplevel->on.handle_destroy.link);
|
|
|
|
/* Compositor side state changes */
|
|
wl_list_remove(&wlr_toplevel->on_view.new_app_id.link);
|
|
wl_list_remove(&wlr_toplevel->on_view.new_title.link);
|
|
wl_list_remove(&wlr_toplevel->on_view.new_outputs.link);
|
|
wl_list_remove(&wlr_toplevel->on_view.maximized.link);
|
|
wl_list_remove(&wlr_toplevel->on_view.minimized.link);
|
|
wl_list_remove(&wlr_toplevel->on_view.fullscreened.link);
|
|
wl_list_remove(&wlr_toplevel->on_view.activated.link);
|
|
|
|
wlr_toplevel->handle = NULL;
|
|
}
|
|
|
|
/* Compositor signals */
|
|
static void
|
|
handle_new_app_id(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on_view.new_app_id);
|
|
assert(wlr_toplevel->handle);
|
|
|
|
wlr_foreign_toplevel_handle_v1_set_app_id(wlr_toplevel->handle,
|
|
wlr_toplevel->view->app_id);
|
|
}
|
|
|
|
static void
|
|
handle_new_title(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on_view.new_title);
|
|
assert(wlr_toplevel->handle);
|
|
|
|
wlr_foreign_toplevel_handle_v1_set_title(wlr_toplevel->handle,
|
|
wlr_toplevel->view->title);
|
|
}
|
|
|
|
static void
|
|
handle_new_outputs(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on_view.new_outputs);
|
|
assert(wlr_toplevel->handle);
|
|
|
|
/*
|
|
* Loop over all outputs and notify foreign_toplevel clients about changes.
|
|
* wlr_foreign_toplevel_handle_v1_output_xxx() keeps track of the active
|
|
* outputs internally and merges the events. It also listens to output
|
|
* destroy events so its fine to just relay the current state and let
|
|
* wlr_foreign_toplevel handle the rest.
|
|
*/
|
|
struct output *output;
|
|
wl_list_for_each(output, &wlr_toplevel->view->server->outputs, link) {
|
|
if (view_on_output(wlr_toplevel->view, output)) {
|
|
wlr_foreign_toplevel_handle_v1_output_enter(
|
|
wlr_toplevel->handle, output->wlr_output);
|
|
} else {
|
|
wlr_foreign_toplevel_handle_v1_output_leave(
|
|
wlr_toplevel->handle, output->wlr_output);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
handle_maximized(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on_view.maximized);
|
|
assert(wlr_toplevel->handle);
|
|
|
|
wlr_foreign_toplevel_handle_v1_set_maximized(wlr_toplevel->handle,
|
|
wlr_toplevel->view->maximized == VIEW_AXIS_BOTH);
|
|
}
|
|
|
|
static void
|
|
handle_minimized(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on_view.minimized);
|
|
assert(wlr_toplevel->handle);
|
|
|
|
wlr_foreign_toplevel_handle_v1_set_minimized(wlr_toplevel->handle,
|
|
wlr_toplevel->view->minimized);
|
|
}
|
|
|
|
static void
|
|
handle_fullscreened(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on_view.fullscreened);
|
|
assert(wlr_toplevel->handle);
|
|
|
|
wlr_foreign_toplevel_handle_v1_set_fullscreen(wlr_toplevel->handle,
|
|
wlr_toplevel->view->fullscreen);
|
|
}
|
|
|
|
static void
|
|
handle_activated(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_foreign_toplevel *wlr_toplevel =
|
|
wl_container_of(listener, wlr_toplevel, on_view.activated);
|
|
assert(wlr_toplevel->handle);
|
|
|
|
bool *activated = data;
|
|
wlr_foreign_toplevel_handle_v1_set_activated(wlr_toplevel->handle,
|
|
*activated);
|
|
}
|
|
|
|
/* Internal API */
|
|
void
|
|
wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel,
|
|
struct view *view)
|
|
{
|
|
assert(view->server->foreign_toplevel_manager);
|
|
wlr_toplevel->view = view;
|
|
|
|
wlr_toplevel->handle = wlr_foreign_toplevel_handle_v1_create(
|
|
view->server->foreign_toplevel_manager);
|
|
if (!wlr_toplevel->handle) {
|
|
wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)",
|
|
view->title);
|
|
return;
|
|
}
|
|
|
|
/* These states may be set before the initial map */
|
|
handle_new_app_id(&wlr_toplevel->on_view.new_app_id, NULL);
|
|
handle_new_title(&wlr_toplevel->on_view.new_title, NULL);
|
|
handle_new_outputs(&wlr_toplevel->on_view.new_outputs, NULL);
|
|
handle_maximized(&wlr_toplevel->on_view.maximized, NULL);
|
|
handle_minimized(&wlr_toplevel->on_view.minimized, NULL);
|
|
handle_fullscreened(&wlr_toplevel->on_view.fullscreened, NULL);
|
|
handle_activated(&wlr_toplevel->on_view.activated,
|
|
&(bool){view == view->server->active_view});
|
|
|
|
/* Client side requests */
|
|
CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_maximize);
|
|
CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_minimize);
|
|
CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_fullscreen);
|
|
CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_activate);
|
|
CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_close);
|
|
wlr_toplevel->on.handle_destroy.notify = handle_handle_destroy;
|
|
wl_signal_add(&wlr_toplevel->handle->events.destroy, &wlr_toplevel->on.handle_destroy);
|
|
|
|
/* Compositor side state changes */
|
|
CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_app_id);
|
|
CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_title);
|
|
CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_outputs);
|
|
CONNECT_SIGNAL(view, &wlr_toplevel->on_view, maximized);
|
|
CONNECT_SIGNAL(view, &wlr_toplevel->on_view, minimized);
|
|
CONNECT_SIGNAL(view, &wlr_toplevel->on_view, fullscreened);
|
|
CONNECT_SIGNAL(view, &wlr_toplevel->on_view, activated);
|
|
}
|
|
|
|
void
|
|
wlr_foreign_toplevel_set_parent(struct wlr_foreign_toplevel *wlr_toplevel,
|
|
struct wlr_foreign_toplevel *parent)
|
|
{
|
|
if (!wlr_toplevel->handle) {
|
|
return;
|
|
}
|
|
|
|
/* The wlroots wlr-foreign-toplevel impl ensures parent is reset to NULL on destroy */
|
|
wlr_foreign_toplevel_handle_v1_set_parent(wlr_toplevel->handle,
|
|
parent ? parent->handle : NULL);
|
|
}
|
|
|
|
void
|
|
wlr_foreign_toplevel_finish(struct wlr_foreign_toplevel *wlr_toplevel)
|
|
{
|
|
if (!wlr_toplevel->handle) {
|
|
return;
|
|
}
|
|
|
|
/* invokes handle_handle_destroy() which does more cleanup */
|
|
wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle);
|
|
assert(!wlr_toplevel->handle);
|
|
}
|