diyac/view.c
2024-04-13 00:25:39 +02:00

383 lines
12 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"
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;
}
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(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);
struct wlr_box geo_box, intersect_box;
struct wlr_box *geometry = &view->original;
// 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);
wlr_log(WLR_DEBUG, "diyac_view_update_geometry: current: [%d,%d,%d,%d], usable: [%d,%d,%d,%d] ",
geometry->x, geometry->y, geometry->width, geometry->height,
usable.x, usable.y, usable.width, usable.height);
struct diyac_server *server = view->server;
wlr_box_intersection(&intersect_box,geometry, &usable);
wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
if ( wlr_box_equal(geometry, &geo_box) && diyac_view_state_equal(view->state,view->requested) && wlr_box_empty(&intersect_box))
{
wlr_log(WLR_INFO, "No geometry update needed");
return false;
}
// 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;
}
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;
if(view->requested.minimized)
{
// TODO implement minimize
return false;
}
else if(view->requested.fullscreen)
{
wlr_scene_node_set_enabled(&view->output->scenes.top->node, false);
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)
{
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
{
// 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);
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;
}
}
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);
return true;
}
}
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);
}