2022-07-06 17:37:22 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
|
|
|
|
|
#define _POSIX_C_SOURCE 200809L
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <float.h>
|
2022-07-06 17:26:22 +02:00
|
|
|
#include <math.h>
|
2022-07-06 17:37:22 +02:00
|
|
|
#include <string.h>
|
2022-07-06 07:19:28 +02:00
|
|
|
#include <wlr/render/pixman.h>
|
2022-07-06 17:37:22 +02:00
|
|
|
#include <wlr/types/wlr_scene.h>
|
|
|
|
|
#include <wlr/util/box.h>
|
|
|
|
|
#include <wlr/util/log.h>
|
|
|
|
|
#include "common/graphic-helpers.h"
|
2022-07-06 16:57:25 +02:00
|
|
|
#include "common/list.h"
|
2022-07-06 17:37:22 +02:00
|
|
|
#include "common/mem.h"
|
|
|
|
|
#include "labwc.h"
|
|
|
|
|
#include "regions.h"
|
2022-07-06 07:19:28 +02:00
|
|
|
#include "view.h"
|
2022-07-06 17:37:22 +02:00
|
|
|
|
2022-07-06 17:21:02 +02:00
|
|
|
bool
|
|
|
|
|
regions_available(void)
|
|
|
|
|
{
|
|
|
|
|
return !wl_list_empty(&rc.regions);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 08:15:33 +02:00
|
|
|
static void
|
|
|
|
|
overlay_create(struct seat *seat)
|
2022-07-06 17:37:22 +02:00
|
|
|
{
|
2022-09-13 08:15:33 +02:00
|
|
|
assert(!seat->region_overlay.tree);
|
|
|
|
|
|
|
|
|
|
struct server *server = seat->server;
|
|
|
|
|
struct wlr_scene_tree *parent = wlr_scene_tree_create(&server->scene->tree);
|
2022-07-06 07:19:28 +02:00
|
|
|
|
2022-09-13 08:15:33 +02:00
|
|
|
seat->region_overlay.tree = parent;
|
|
|
|
|
wlr_scene_node_set_enabled(&parent->node, false);
|
|
|
|
|
if (!wlr_renderer_is_pixman(server->renderer)) {
|
|
|
|
|
/* Hardware assisted rendering: Half transparent overlay */
|
|
|
|
|
float color[4] = { 0.25, 0.25, 0.35, 0.5 };
|
|
|
|
|
seat->region_overlay.overlay = wlr_scene_rect_create(parent, 0, 0, color);
|
2022-07-06 07:19:28 +02:00
|
|
|
} else {
|
2022-09-13 08:15:33 +02:00
|
|
|
/* Software rendering: Outlines */
|
|
|
|
|
int line_width = server->theme->osd_border_width;
|
|
|
|
|
float *colors[3] = {
|
|
|
|
|
server->theme->osd_bg_color,
|
|
|
|
|
server->theme->osd_label_text_color,
|
|
|
|
|
server->theme->osd_bg_color
|
|
|
|
|
};
|
|
|
|
|
seat->region_overlay.pixman_overlay = multi_rect_create(parent, colors, line_width);
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
2022-07-06 17:37:22 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-06 08:06:48 +02:00
|
|
|
struct region *
|
|
|
|
|
regions_from_name(const char *region_name, struct output *output)
|
|
|
|
|
{
|
|
|
|
|
assert(region_name);
|
|
|
|
|
assert(output);
|
|
|
|
|
struct region *region;
|
|
|
|
|
wl_list_for_each(region, &output->regions, link) {
|
|
|
|
|
if (!strcmp(region->name, region_name)) {
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 17:26:22 +02:00
|
|
|
struct region *
|
|
|
|
|
regions_from_cursor(struct server *server)
|
|
|
|
|
{
|
|
|
|
|
assert(server);
|
|
|
|
|
double lx = server->seat.cursor->x;
|
|
|
|
|
double ly = server->seat.cursor->y;
|
|
|
|
|
|
|
|
|
|
struct wlr_output *wlr_output = wlr_output_layout_output_at(
|
|
|
|
|
server->output_layout, lx, ly);
|
|
|
|
|
struct output *output = output_from_wlr_output(server, wlr_output);
|
|
|
|
|
if (!output) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double dist;
|
|
|
|
|
double dist_min = DBL_MAX;
|
|
|
|
|
struct region *closest_region = NULL;
|
|
|
|
|
struct region *region;
|
|
|
|
|
wl_list_for_each(region, &output->regions, link) {
|
|
|
|
|
if (wlr_box_contains_point(®ion->geo, lx, ly)) {
|
|
|
|
|
/* No need for sqrt((x1 - x2)^2 + (y1 - y2)^2) as we just compare */
|
|
|
|
|
dist = pow(region->center.x - lx, 2) + pow(region->center.y - ly, 2);
|
|
|
|
|
if (dist < dist_min) {
|
|
|
|
|
closest_region = region;
|
|
|
|
|
dist_min = dist;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return closest_region;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 07:19:28 +02:00
|
|
|
void
|
|
|
|
|
regions_show_overlay(struct view *view, struct seat *seat, struct region *region)
|
|
|
|
|
{
|
|
|
|
|
assert(view);
|
|
|
|
|
assert(seat);
|
|
|
|
|
assert(region);
|
|
|
|
|
|
2022-09-13 08:15:33 +02:00
|
|
|
/* Don't show active region */
|
|
|
|
|
if (seat->region_active == region) {
|
|
|
|
|
return;
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
2022-09-13 08:15:33 +02:00
|
|
|
|
|
|
|
|
if (!seat->region_overlay.tree) {
|
|
|
|
|
overlay_create(seat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update overlay */
|
|
|
|
|
struct server *server = seat->server;
|
|
|
|
|
struct wlr_scene_node *node = &seat->region_overlay.tree->node;
|
|
|
|
|
if (!wlr_renderer_is_pixman(server->renderer)) {
|
|
|
|
|
/* Hardware assisted rendering: Half transparent overlay */
|
|
|
|
|
wlr_scene_rect_set_size(seat->region_overlay.overlay,
|
|
|
|
|
region->geo.width, region->geo.height);
|
|
|
|
|
} else {
|
|
|
|
|
/* Software rendering: Outlines */
|
|
|
|
|
multi_rect_set_size(seat->region_overlay.pixman_overlay,
|
|
|
|
|
region->geo.width, region->geo.height);
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
2022-09-13 08:15:33 +02:00
|
|
|
if (node->parent != view->scene_tree->node.parent) {
|
|
|
|
|
wlr_scene_node_reparent(node, view->scene_tree->node.parent);
|
|
|
|
|
wlr_scene_node_place_below(node, &view->scene_tree->node);
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
2022-09-13 08:15:33 +02:00
|
|
|
wlr_scene_node_set_position(node, region->geo.x, region->geo.y);
|
|
|
|
|
wlr_scene_node_set_enabled(node, true);
|
|
|
|
|
seat->region_active = region;
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-09-13 08:15:33 +02:00
|
|
|
regions_hide_overlay(struct seat *seat)
|
2022-07-06 07:19:28 +02:00
|
|
|
{
|
|
|
|
|
assert(seat);
|
2022-09-13 08:15:33 +02:00
|
|
|
if (!seat->region_active) {
|
|
|
|
|
return;
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
2022-09-13 08:15:33 +02:00
|
|
|
|
|
|
|
|
struct server *server = seat->server;
|
|
|
|
|
struct wlr_scene_node *node = &seat->region_overlay.tree->node;
|
|
|
|
|
|
|
|
|
|
wlr_scene_node_set_enabled(node, false);
|
|
|
|
|
if (node->parent != &server->scene->tree) {
|
|
|
|
|
wlr_scene_node_reparent(node, &server->scene->tree);
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
2022-09-13 08:15:33 +02:00
|
|
|
seat->region_active = NULL;
|
2022-07-06 07:19:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-06 17:37:22 +02:00
|
|
|
void
|
2023-01-01 18:12:20 +01:00
|
|
|
regions_reconfigure_output(struct output *output)
|
2022-07-06 17:37:22 +02:00
|
|
|
{
|
2022-07-06 16:57:25 +02:00
|
|
|
assert(output);
|
|
|
|
|
|
2023-01-01 18:12:20 +01:00
|
|
|
/* Evacuate views and destroy current regions */
|
|
|
|
|
if (!wl_list_empty(&output->regions)) {
|
|
|
|
|
regions_evacuate_output(output);
|
|
|
|
|
regions_destroy(&output->server->seat, &output->regions);
|
|
|
|
|
}
|
2022-07-06 16:57:25 +02:00
|
|
|
|
2023-01-01 18:12:20 +01:00
|
|
|
/* Initialize regions from config */
|
|
|
|
|
struct region *region;
|
|
|
|
|
wl_list_for_each(region, &rc.regions, link) {
|
|
|
|
|
struct region *region_new = znew(*region_new);
|
|
|
|
|
/* Create a copy */
|
|
|
|
|
region_new->name = xstrdup(region->name);
|
|
|
|
|
region_new->percentage = region->percentage;
|
|
|
|
|
wl_list_append(&output->regions, ®ion_new->link);
|
2022-07-06 16:57:25 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-01 18:12:20 +01:00
|
|
|
/* Update region geometries */
|
|
|
|
|
regions_update_geometry(output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
regions_reconfigure(struct server *server)
|
|
|
|
|
{
|
|
|
|
|
struct output *output;
|
|
|
|
|
|
|
|
|
|
/* Evacuate views and initialize regions from config */
|
|
|
|
|
wl_list_for_each(output, &server->outputs, link) {
|
|
|
|
|
regions_reconfigure_output(output);
|
2022-09-13 08:15:33 +02:00
|
|
|
}
|
2023-01-01 18:12:20 +01:00
|
|
|
|
|
|
|
|
/* Tries to match the evacuated views to the new regions */
|
|
|
|
|
desktop_arrange_all_views(server);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
regions_update_geometry(struct output *output)
|
|
|
|
|
{
|
|
|
|
|
assert(output);
|
|
|
|
|
|
|
|
|
|
struct region *region;
|
|
|
|
|
struct wlr_box usable = output_usable_area_in_layout_coords(output);
|
2022-09-13 08:15:33 +02:00
|
|
|
|
2022-07-06 16:57:25 +02:00
|
|
|
/* Update regions */
|
|
|
|
|
struct wlr_box *perc, *geo;
|
|
|
|
|
wl_list_for_each(region, &output->regions, link) {
|
|
|
|
|
geo = ®ion->geo;
|
|
|
|
|
perc = ®ion->percentage;
|
|
|
|
|
geo->x = usable.x + usable.width * perc->x / 100;
|
|
|
|
|
geo->y = usable.y + usable.height * perc->y / 100;
|
|
|
|
|
geo->width = usable.width * perc->width / 100;
|
|
|
|
|
geo->height = usable.height * perc->height / 100;
|
|
|
|
|
region->center.x = geo->x + geo->width / 2;
|
|
|
|
|
region->center.y = geo->y + geo->height / 2;
|
|
|
|
|
}
|
2022-07-06 17:37:22 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-07 19:05:54 +02:00
|
|
|
void
|
|
|
|
|
regions_evacuate_output(struct output *output)
|
|
|
|
|
{
|
|
|
|
|
assert(output);
|
|
|
|
|
struct view *view;
|
|
|
|
|
struct region *region;
|
|
|
|
|
|
|
|
|
|
wl_list_for_each(view, &output->server->views, link) {
|
|
|
|
|
wl_list_for_each(region, &output->regions, link) {
|
|
|
|
|
if (view->tiled_region == region) {
|
|
|
|
|
if (!view->tiled_region_evacuate) {
|
|
|
|
|
view->tiled_region_evacuate =
|
|
|
|
|
xstrdup(region->name);
|
|
|
|
|
}
|
2023-01-01 18:12:20 +01:00
|
|
|
/* Prevent carrying around a dangling pointer */
|
|
|
|
|
view->tiled_region = NULL;
|
2022-07-07 19:05:54 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 17:37:22 +02:00
|
|
|
void
|
2022-09-13 08:15:33 +02:00
|
|
|
regions_destroy(struct seat *seat, struct wl_list *regions)
|
2022-07-06 17:37:22 +02:00
|
|
|
{
|
|
|
|
|
assert(regions);
|
|
|
|
|
struct region *region, *region_tmp;
|
|
|
|
|
wl_list_for_each_safe(region, region_tmp, regions, link) {
|
|
|
|
|
wl_list_remove(®ion->link);
|
|
|
|
|
zfree(region->name);
|
2022-09-13 08:15:33 +02:00
|
|
|
if (seat && seat->region_active == region) {
|
|
|
|
|
seat->region_active = NULL;
|
|
|
|
|
}
|
2022-07-06 17:37:22 +02:00
|
|
|
zfree(region);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|