461 lines
14 KiB
C
461 lines
14 KiB
C
#define _POSIX_C_SOURCE 200112L
|
|
#include <wlr/util/log.h>
|
|
#include <assert.h>
|
|
#include "view.h"
|
|
#include "node.h"
|
|
#include "seat.h"
|
|
#include "output.h"
|
|
#include "cursor.h"
|
|
|
|
#define CONFIGURE_TIMEOUT_MS 150
|
|
static int handle_configure_timeout(void *data)
|
|
{
|
|
struct diyac_view *view = data;
|
|
assert(view->configuration_serial > 0);
|
|
assert(view->configuration_timeout);
|
|
|
|
wlr_log(WLR_INFO, "client (%s) did not respond to configure request "
|
|
"in %d ms",
|
|
view->xdg_toplevel->app_id, CONFIGURE_TIMEOUT_MS);
|
|
|
|
wl_event_source_remove(view->configuration_timeout);
|
|
view->configuration_serial = 0;
|
|
view->configuration_timeout = NULL;
|
|
|
|
diyac_view_sync_geo(view);
|
|
return 0; /* ignored per wl_event_loop docs */
|
|
}
|
|
|
|
static void diyac_view_configure(struct diyac_view *view, struct wlr_box geo)
|
|
{
|
|
view->pending_size = geo;
|
|
view->configuration_serial = wlr_xdg_toplevel_set_size(view->xdg_toplevel, geo.width, geo.height);
|
|
if (!view->configuration_timeout)
|
|
{
|
|
view->configuration_timeout = wl_event_loop_add_timer(view->server->wl_event_loop, handle_configure_timeout, view);
|
|
}
|
|
wl_event_source_timer_update(view->configuration_timeout, CONFIGURE_TIMEOUT_MS);
|
|
}
|
|
|
|
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 */
|
|
wlr_scene_node_raise_to_top(&view->scene_tree->node);
|
|
wl_list_remove(&view->link);
|
|
wl_list_insert(&view->server->views, &view->link);
|
|
/* Activate the new surface */
|
|
}
|
|
|
|
void diyac_focus_view(struct diyac_view *toplevel, bool raise)
|
|
{
|
|
/* Note: this function only deals with keyboard focus. */
|
|
if (toplevel == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (!toplevel->mapped)
|
|
{
|
|
// dont focus unmapped view
|
|
return;
|
|
}
|
|
struct wlr_surface *prev_surface = toplevel->server->seat.wlr_seat->keyboard_state.focused_surface;
|
|
if (prev_surface == toplevel->xdg_toplevel->base->surface)
|
|
{
|
|
// Don't re-focus an already focused surface.
|
|
wlr_log(WLR_DEBUG, "Don't re-focus an already focused surface");
|
|
return;
|
|
}
|
|
if (toplevel->server->active_view)
|
|
{
|
|
diyac_view_set_activated(toplevel->server->active_view, false);
|
|
}
|
|
raise_to_front(toplevel);
|
|
if (raise)
|
|
{
|
|
struct diyac_view **subview = NULL;
|
|
struct wl_array subviews;
|
|
wl_array_init(&subviews);
|
|
diyac_get_children_views(toplevel, &subviews);
|
|
wl_array_for_each(subview, &subviews)
|
|
{
|
|
raise_to_front(*subview);
|
|
}
|
|
wl_array_release(&subviews);
|
|
}
|
|
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(
|
|
struct diyac_server *server, double lx, double ly,
|
|
struct wlr_surface **surface, double *sx, double *sy)
|
|
{
|
|
struct diyac_node_descriptor *node_descriptor = diyac_node_at(server, lx, ly, surface, sx, sy);
|
|
if (!node_descriptor || node_descriptor->type != DIYAC_NODE_VIEW)
|
|
{
|
|
return NULL;
|
|
}
|
|
return node_descriptor->data;
|
|
}
|
|
|
|
void diyac_focus_topmost_view(struct diyac_server *server, bool raise)
|
|
{
|
|
struct diyac_view *view = diyac_topmost_focusable_view(server);
|
|
if (view)
|
|
{
|
|
if (raise)
|
|
{
|
|
struct diyac_view *root = diyac_get_root_view(view);
|
|
if (root)
|
|
{
|
|
diyac_focus_view(root, true);
|
|
return;
|
|
}
|
|
}
|
|
diyac_focus_view(view, false);
|
|
}
|
|
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).
|
|
*/
|
|
diyac_seat_focus_surface(&server->seat, NULL);
|
|
}
|
|
}
|
|
|
|
struct diyac_view *diyac_topmost_focusable_view(struct diyac_server *server)
|
|
{
|
|
struct wlr_surface *prev =
|
|
server->seat.wlr_seat->keyboard_state.focused_surface;
|
|
struct diyac_view *view;
|
|
struct wl_list *node_list;
|
|
struct wlr_scene_node *node;
|
|
node_list = &server->view_tree->children;
|
|
wl_list_for_each_reverse(node, node_list, link)
|
|
{
|
|
if (!node->data)
|
|
{
|
|
/* We found some non-view, most likely the region overlay */
|
|
continue;
|
|
}
|
|
view = diyac_view_from_node(node);
|
|
|
|
if (view->mapped /*&& view_is_focusable_from(view, prev)*/)
|
|
{
|
|
return view;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void diyac_arrange_all_views(struct diyac_server *server)
|
|
{
|
|
/*
|
|
* Adjust window positions/sizes. Skip views with no size since
|
|
* we can't do anything useful with them; they will presumably
|
|
* be initialized with valid positions/sizes later.
|
|
*
|
|
* We do not simply check view->mapped/been_mapped here because
|
|
* views can have maximized/fullscreen geometry applied while
|
|
* still unmapped. We do want to adjust the geometry of those
|
|
* views.
|
|
*/
|
|
struct diyac_view *view;
|
|
wl_list_for_each(view, &server->views, link)
|
|
{
|
|
diyac_view_update_geometry(view, false);
|
|
}
|
|
}
|
|
bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed)
|
|
{
|
|
assert(view);
|
|
// if (wlr_output_layout_intersects(view->server->output_layout,
|
|
// view->output->wlr_output, &view->current))
|
|
//{
|
|
if (!view->mapped)
|
|
{
|
|
wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
|
|
return false;
|
|
}
|
|
struct wlr_box usable = diyac_output_usable_area(view->output);
|
|
struct diyac_server *server = view->server;
|
|
// invalidate old state if change state
|
|
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;
|
|
view->pending_size = view->original;
|
|
}
|
|
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->pending_size = view->original;
|
|
view->state.maximized = false;
|
|
}
|
|
bool updated = false;
|
|
if (view->requested.minimized)
|
|
{
|
|
// TODO implement minimize
|
|
return false;
|
|
}
|
|
else if (!view->state.fullscreen && view->requested.fullscreen)
|
|
{
|
|
struct wlr_box box = {0};
|
|
wlr_output_effective_resolution(view->output->wlr_output,
|
|
&box.width, &box.height);
|
|
double ox = 0, oy = 0;
|
|
wlr_output_layout_output_coords(view->server->output_layout,
|
|
view->output->wlr_output, &ox, &oy);
|
|
box.x -= ox;
|
|
box.y -= oy;
|
|
wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, true);
|
|
wlr_scene_node_set_enabled(&view->output->scenes.top->node, false);
|
|
if (view->toplevel.handle)
|
|
{
|
|
wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel.handle, true);
|
|
}
|
|
diyac_view_configure(view, box);
|
|
updated = true;
|
|
}
|
|
else if (view->requested.maximized)
|
|
{
|
|
wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, true);
|
|
if (view->toplevel.handle)
|
|
{
|
|
wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel.handle, true);
|
|
}
|
|
diyac_view_configure(view, usable);
|
|
updated = true;
|
|
}
|
|
else
|
|
{
|
|
// if (wlr_output_layout_intersects(view->server->output_layout,
|
|
// view->output->wlr_output, &view->current))
|
|
//{
|
|
/**Normal state, recalculate current geometry*/
|
|
struct diyac_view *root = diyac_get_root_view(view);
|
|
struct wlr_box geometry = view->pending_size;
|
|
if (!root)
|
|
{
|
|
root = view;
|
|
}
|
|
if (!root->state.fullscreen)
|
|
{
|
|
|
|
// Only adjust position only when not in fullscreen mode
|
|
|
|
if (!grabbed && geometry.x < usable.x)
|
|
{
|
|
geometry.x = usable.x;
|
|
}
|
|
if (!grabbed && geometry.y < usable.y)
|
|
{
|
|
geometry.y = usable.y;
|
|
}
|
|
if (grabbed && server->seat.cursor->x <= usable.x)
|
|
{
|
|
geometry.x = usable.x - server->grab_x;
|
|
}
|
|
if (grabbed && server->seat.cursor->y <= usable.y)
|
|
{
|
|
geometry.y = usable.y;
|
|
}
|
|
if (grabbed && server->seat.cursor->x >= usable.x + usable.width)
|
|
{
|
|
geometry.x = usable.x + usable.width - server->grab_x;
|
|
}
|
|
if (grabbed && server->seat.cursor->y >= usable.y + usable.height)
|
|
{
|
|
geometry.y = usable.y + usable.height - server->grab_y;
|
|
}
|
|
}
|
|
if (!wlr_box_equal(&geometry, &view->original) || !view->requested.maximized)
|
|
{
|
|
wlr_log(WLR_DEBUG, "diyac_view_update_geometry: updating geometry: %d %d %d %d", geometry.x, geometry.y, geometry.width, geometry.height);
|
|
// wlr_scene_node_set_position(&view->scene_tree->node, geometry.x, geometry.y);
|
|
// wlr_xdg_toplevel_set_size(view->xdg_toplevel, geometry.width, geometry.height);
|
|
diyac_view_configure(view, geometry);
|
|
updated = true;
|
|
}
|
|
}
|
|
view->state = view->requested;
|
|
return updated;
|
|
}
|
|
|
|
void diyac_get_children_views(struct diyac_view *view, struct wl_array *children)
|
|
{
|
|
struct diyac_view *child;
|
|
wl_list_for_each_reverse(child, &view->server->views, link)
|
|
{
|
|
if (child == view)
|
|
{
|
|
continue;
|
|
}
|
|
if (!child->mapped /*&& !view->minimized*/)
|
|
{
|
|
continue;
|
|
}
|
|
if (diyac_get_root_view(child) != view)
|
|
{
|
|
continue;
|
|
}
|
|
struct diyac_view **item = wl_array_add(children, sizeof(*item));
|
|
*item = child;
|
|
}
|
|
}
|
|
|
|
struct diyac_view *diyac_get_root_view(struct diyac_view *view)
|
|
{
|
|
struct wlr_xdg_toplevel *toplevel = view->xdg_toplevel;
|
|
while (toplevel->parent)
|
|
{
|
|
toplevel = toplevel->parent;
|
|
}
|
|
return toplevel->base->data;
|
|
}
|
|
|
|
struct wlr_box diyac_view_get_geometry(struct diyac_view *view)
|
|
{
|
|
struct wlr_box box;
|
|
if (view->state.fullscreen)
|
|
{
|
|
wlr_output_layout_get_box(view->server->output_layout, view->output->wlr_output, &box);
|
|
return box;
|
|
}
|
|
if (view->state.maximized)
|
|
{
|
|
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);
|
|
}
|
|
|
|
void diyac_view_update_title(struct diyac_view *view)
|
|
{
|
|
struct wlr_xdg_toplevel *xdg_toplevel = view->xdg_toplevel;
|
|
if (!xdg_toplevel)
|
|
{
|
|
return;
|
|
}
|
|
const char *title = xdg_toplevel->title;
|
|
wlr_log(WLR_INFO, "diyac_view_update_title: %s", title ? title : "");
|
|
if (!view->toplevel.handle || !title)
|
|
{
|
|
return;
|
|
}
|
|
wlr_foreign_toplevel_handle_v1_set_title(view->toplevel.handle, title);
|
|
}
|
|
void diyac_view_update_app_id(struct diyac_view *view)
|
|
{
|
|
struct wlr_xdg_toplevel *xdg_toplevel = view->xdg_toplevel;
|
|
if (!xdg_toplevel)
|
|
{
|
|
return;
|
|
}
|
|
const char *appid = xdg_toplevel->app_id;
|
|
wlr_log(WLR_INFO, "diyac_view_update_app_id: %s", appid ? appid : "");
|
|
if (!view->toplevel.handle || !appid)
|
|
{
|
|
return;
|
|
}
|
|
wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel.handle, appid);
|
|
}
|
|
|
|
void diyac_view_sync_geo(struct diyac_view *view)
|
|
{
|
|
struct wlr_box size;
|
|
wlr_xdg_surface_get_geometry(view->xdg_surface, &size);
|
|
if (!view->state.fullscreen && !view->state.maximized)
|
|
{
|
|
|
|
if ((view->server->resize_edges & WLR_EDGE_LEFT) || (
|
|
(view->original.x != view->pending_size.x) &&
|
|
(view->original.x + view->original.width == view->pending_size.x + view->pending_size.width)))
|
|
{
|
|
view->original.x = view->pending_size.x + view->pending_size.width - size.width;
|
|
}
|
|
else
|
|
{
|
|
view->original.x = view->pending_size.x;
|
|
}
|
|
if ((view->server->resize_edges & WLR_EDGE_TOP) || (
|
|
(view->original.y != view->pending_size.y) &&
|
|
(view->original.y + view->original.height == view->pending_size.y + view->pending_size.height)))
|
|
{
|
|
view->original.y = view->pending_size.y + view->pending_size.height - size.height;
|
|
}
|
|
else
|
|
{
|
|
view->original.y = view->pending_size.y;
|
|
}
|
|
|
|
view->original.width = size.width;
|
|
view->original.height = size.height;
|
|
wlr_scene_node_set_position(&view->scene_tree->node, view->original.x, view->original.y);
|
|
view->pending_size = view->original;
|
|
}
|
|
else
|
|
{
|
|
if(wlr_box_equal(&size, &view->pending_size))
|
|
{
|
|
return;
|
|
}
|
|
//wlr_log(WLR_ERROR, "update fullscreen position");
|
|
wlr_scene_node_set_position(&view->scene_tree->node, view->pending_size.x, view->pending_size.y);
|
|
}
|
|
}
|