labwc/src/view.c

750 lines
18 KiB
C
Raw Normal View History

2021-09-24 21:45:48 +01:00
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <stdio.h>
#include <strings.h>
2022-04-20 18:48:49 +01:00
#include <xcb/xcb_icccm.h>
2021-03-20 14:36:40 +00:00
#include "labwc.h"
2021-03-21 20:54:55 +00:00
#include "ssd.h"
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
void
view_set_activated(struct view *view, bool activated)
{
2022-02-21 03:18:38 +01:00
if (view->ssd.enabled) {
ssd_set_active(view);
}
if (view->impl->set_activated) {
view->impl->set_activated(view, activated);
}
if (view->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_set_activated(
view->toplevel_handle, activated);
}
}
2021-12-23 12:24:24 +01:00
void
view_close(struct view *view)
{
if (view->impl->close) {
view->impl->close(view);
}
}
void
view_move(struct view *view, double x, double y)
{
if (view->impl->move) {
view->impl->move(view, x, y);
}
view_discover_output(view);
wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
ssd_update_geometry(view);
}
/* N.B. Use view_move() if not resizing. */
2020-12-23 18:52:46 +00:00
void
view_move_resize(struct view *view, struct wlr_box geo)
2020-12-23 18:52:46 +00:00
{
if (view->impl->configure) {
view->impl->configure(view, geo);
}
ssd_update_title(view);
view_discover_output(view);
2020-12-23 18:52:46 +00:00
}
#define MIN_VIEW_WIDTH (100)
#define MIN_VIEW_HEIGHT (60)
#if HAVE_XWAYLAND
static int
round_to_increment(int val, int base, int inc)
{
if (base < 0 || inc <= 0)
return val;
return base + (val - base + inc / 2) / inc * inc;
}
#endif
void
view_adjust_size(struct view *view, int *w, int *h)
{
int min_width = MIN_VIEW_WIDTH;
int min_height = MIN_VIEW_HEIGHT;
#if HAVE_XWAYLAND
if (view->type == LAB_XWAYLAND_VIEW) {
2022-04-20 18:48:49 +01:00
xcb_size_hints_t *hints = view->xwayland_surface->size_hints;
/*
* Honor size increments from WM_SIZE_HINTS. Typically, X11
* terminal emulators will use WM_SIZE_HINTS to make sure that
* the terminal is resized to a width/height evenly divisible by
* the cell (character) size.
*/
if (hints) {
*w = round_to_increment(*w, hints->base_width,
hints->width_inc);
*h = round_to_increment(*h, hints->base_height,
hints->height_inc);
min_width = MAX(1, hints->min_width);
min_height = MAX(1, hints->min_height);
}
}
#endif
*w = MAX(*w, min_width);
*h = MAX(*h, min_height);
}
void
2021-08-05 13:00:34 +01:00
view_minimize(struct view *view, bool minimized)
2020-09-08 20:51:33 +01:00
{
2021-08-05 13:00:34 +01:00
if (view->minimized == minimized) {
2020-09-08 20:51:33 +01:00
return;
}
2021-08-05 13:00:34 +01:00
if (view->toplevel_handle) {
2021-11-26 18:49:44 +00:00
wlr_foreign_toplevel_handle_v1_set_minimized(
view->toplevel_handle, minimized);
2021-08-05 13:00:34 +01:00
}
view->minimized = minimized;
if (minimized) {
view->impl->unmap(view);
desktop_move_to_back(view);
view_set_activated(view, false);
2021-08-05 13:00:34 +01:00
} else {
view->impl->map(view);
}
2020-09-08 20:51:33 +01:00
}
2020-09-29 20:48:50 +01:00
/* view_wlr_output - return the output that a view is mostly on */
struct wlr_output *
view_wlr_output(struct view *view)
2021-10-18 19:59:11 -04:00
{
2021-10-19 18:15:56 -04:00
double closest_x, closest_y;
struct wlr_output *wlr_output = NULL;
wlr_output_layout_closest_point(view->server->output_layout, wlr_output,
view->x + view->w / 2, view->y + view->h / 2, &closest_x,
&closest_y);
wlr_output = wlr_output_layout_output_at(view->server->output_layout,
closest_x, closest_y);
return wlr_output;
2021-10-18 19:59:11 -04:00
}
static struct output *
view_output(struct view *view)
{
struct wlr_output *wlr_output = view_wlr_output(view);
return output_from_wlr_output(view->server, wlr_output);
}
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
static bool
view_compute_centered_position(struct view *view, int w, int h, int *x, int *y)
2021-07-09 21:39:20 +01:00
{
struct output *output = view_output(view);
if (!output) {
return false;
}
struct wlr_output *wlr_output = output->wlr_output;
if (!wlr_output) {
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
return false;
2021-07-09 21:39:20 +01:00
}
struct wlr_box usable = output_usable_area_in_layout_coords(output);
int width = w + view->margin.left + view->margin.right;
int height = h + view->margin.top + view->margin.bottom;
*x = usable.x + (usable.width - width) / 2;
*y = usable.y + (usable.height - height) / 2;
/* If view is bigger than usable area, just top/left align it */
if (*x < rc.gap) {
*x = rc.gap;
}
if (*y < rc.gap) {
*y = rc.gap;
}
#if HAVE_XWAYLAND
/* TODO: refactor xwayland.c functions to get rid of this */
if (view->type == LAB_XWAYLAND_VIEW) {
*x += view->margin.left;
*y += view->margin.top;
}
#endif
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
return true;
}
void
view_center(struct view *view)
{
int x, y;
if (view_compute_centered_position(view, view->w, view->h, &x, &y)) {
view_move(view, x, y);
}
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
}
static void
view_apply_fullscreen_geometry(struct view *view, struct wlr_output *wlr_output)
{
struct output *output =
output_from_wlr_output(view->server, wlr_output);
struct wlr_box box = { 0 };
wlr_output_effective_resolution(wlr_output, &box.width, &box.height);
double ox = 0, oy = 0;
wlr_output_layout_output_coords(output->server->output_layout,
output->wlr_output, &ox, &oy);
box.x -= ox;
box.y -= oy;
view_move_resize(view, box);
}
static void
view_apply_maximized_geometry(struct view *view)
{
/*
* The same code handles both initial maximize and re-maximize
* to account for layout changes. In either case, view_output()
* gives the output closest to the current geometry (which may
* be different from the output originally maximized onto).
* view_output() can return NULL if all outputs are disabled.
*/
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
struct output *output = view_output(view);
if (!output) {
return;
}
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
struct wlr_box box = output_usable_area_in_layout_coords(output);
2022-04-04 20:53:36 +01:00
if (box.height == output->wlr_output->height
&& output->wlr_output->scale != 1) {
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
box.height /= output->wlr_output->scale;
}
2022-04-04 20:53:36 +01:00
if (box.width == output->wlr_output->width
&& output->wlr_output->scale != 1) {
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
box.width /= output->wlr_output->scale;
}
if (view->ssd.enabled) {
struct border border = ssd_thickness(view);
box.x += border.left;
box.y += border.top;
box.width -= border.right + border.left;
box.height -= border.top + border.bottom;
}
view_move_resize(view, box);
}
#define LAB_FALLBACK_WIDTH (640)
#define LAB_FALLBACK_HEIGHT (480)
static void
set_fallback_geometry(struct view *view)
{
view->unmaximized_geometry.width = LAB_FALLBACK_WIDTH;
view->unmaximized_geometry.height = LAB_FALLBACK_HEIGHT;
view_compute_centered_position(view,
view->unmaximized_geometry.width,
view->unmaximized_geometry.height,
&view->unmaximized_geometry.x,
&view->unmaximized_geometry.y);
}
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
static void
view_apply_unmaximized_geometry(struct view *view)
{
/*
* If an application was started maximized, its unmaximized_geometry
* width/height may still be zero in which case we set some fallback
* values. This is the case with foot and Qt applications.
*/
if (wlr_box_empty(&view->unmaximized_geometry)) {
set_fallback_geometry(view);
}
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
struct wlr_output_layout *layout = view->server->output_layout;
2022-04-04 20:53:36 +01:00
if (wlr_output_layout_intersects(layout, NULL,
&view->unmaximized_geometry)) {
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
/* restore to original geometry */
view_move_resize(view, view->unmaximized_geometry);
} else {
/* reposition if original geometry is offscreen */
struct wlr_box box = view->unmaximized_geometry;
if (view_compute_centered_position(view, box.width, box.height,
&box.x, &box.y)) {
view_move_resize(view, box);
}
}
2021-07-09 21:39:20 +01:00
}
void
view_maximize(struct view *view, bool maximize)
{
2021-03-12 21:27:17 +00:00
if (view->maximized == maximize) {
return;
}
if (view->fullscreen) {
return;
}
if (view->impl->maximize) {
view->impl->maximize(view, maximize);
}
2021-08-05 12:52:42 +01:00
if (view->toplevel_handle) {
2021-11-26 18:49:44 +00:00
wlr_foreign_toplevel_handle_v1_set_maximized(
view->toplevel_handle, maximize);
2021-08-05 12:52:42 +01:00
}
2021-03-12 21:27:17 +00:00
if (maximize) {
interactive_end(view);
2021-02-28 18:12:10 +00:00
view->unmaximized_geometry.x = view->x;
view->unmaximized_geometry.y = view->y;
view->unmaximized_geometry.width = view->w;
view->unmaximized_geometry.height = view->h;
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
view_apply_maximized_geometry(view);
view->maximized = true;
2021-02-28 18:12:10 +00:00
} else {
/* unmaximize */
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
view_apply_unmaximized_geometry(view);
view->maximized = false;
}
}
2021-08-02 16:49:41 +01:00
void
view_toggle_maximize(struct view *view)
{
view_maximize(view, !view->maximized);
}
void
view_toggle_decorations(struct view *view)
{
if (!view->fullscreen) {
view->ssd.enabled = !view->ssd.enabled;
2022-02-21 03:18:38 +01:00
ssd_update_geometry(view);
if (view->maximized) {
view_apply_maximized_geometry(view);
}
}
}
2022-04-09 01:16:09 +02:00
static bool
is_always_on_top(struct view *view)
{
return view->scene_tree->node.parent ==
&view->server->view_tree_always_on_top->node;
}
void
view_toggle_always_on_top(struct view *view)
{
if (is_always_on_top(view)) {
wlr_scene_node_reparent(&view->scene_tree->node,
&view->server->view_tree->node);
} else {
wlr_scene_node_reparent(&view->scene_tree->node,
&view->server->view_tree_always_on_top->node);
}
}
void
view_set_decorations(struct view *view, bool decorations)
{
if (view->ssd.enabled != decorations && !view->fullscreen) {
view->ssd.enabled = decorations;
2022-02-21 03:18:38 +01:00
ssd_update_geometry(view);
if (view->maximized) {
view_apply_maximized_geometry(view);
}
}
}
void
view_toggle_fullscreen(struct view *view)
{
view_set_fullscreen(view, !view->fullscreen, NULL);
}
2021-08-23 22:05:30 +01:00
void
view_set_fullscreen(struct view *view, bool fullscreen,
struct wlr_output *wlr_output)
{
2022-04-04 20:53:36 +01:00
if (fullscreen != !view->fullscreen) {
2021-08-23 22:05:30 +01:00
return;
}
if (view->impl->set_fullscreen) {
view->impl->set_fullscreen(view, fullscreen);
2021-08-23 22:05:30 +01:00
}
if (view->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_set_fullscreen(
view->toplevel_handle, fullscreen);
}
if (!wlr_output) {
wlr_output = view_wlr_output(view);
}
2021-08-23 22:05:30 +01:00
if (fullscreen) {
if (!view->maximized) {
view->unmaximized_geometry.x = view->x;
view->unmaximized_geometry.y = view->y;
view->unmaximized_geometry.width = view->w;
view->unmaximized_geometry.height = view->h;
}
2021-08-23 22:05:30 +01:00
view->fullscreen = wlr_output;
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
view_apply_fullscreen_geometry(view, view->fullscreen);
2021-08-23 22:05:30 +01:00
} else {
/* restore to normal */
if (view->maximized) {
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
view_apply_maximized_geometry(view);
} else {
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
view_apply_unmaximized_geometry(view);
}
2021-08-23 22:05:30 +01:00
view->fullscreen = false;
}
/* Show fullscreen views above top-layer */
struct output *output =
output_from_wlr_output(view->server, wlr_output);
uint32_t top = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
wlr_scene_node_set_enabled(&output->layer_tree[top]->node, !fullscreen);
2021-08-23 22:05:30 +01:00
}
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
void
view_adjust_for_layout_change(struct view *view)
{
struct wlr_output_layout *layout = view->server->output_layout;
if (view->fullscreen) {
if (wlr_output_layout_get(layout, view->fullscreen)) {
/* recompute fullscreen geometry */
view_apply_fullscreen_geometry(view, view->fullscreen);
} else {
/* output is gone, exit fullscreen */
view_set_fullscreen(view, false, NULL);
}
} else if (view->maximized) {
/* recompute maximized geometry */
view_apply_maximized_geometry(view);
} else {
/* reposition view if it's offscreen */
struct wlr_box box = { view->x, view->y, view->w, view->h };
if (!wlr_output_layout_intersects(layout, NULL, &box)) {
view_center(view);
}
Adjust views to account for output layout changes labwc currently doesn't handle output layout changes very well: - Windows can end up "lost" completely offscreen - Maximized/fullscreen windows can end up spanning multiple outputs Currently, new_output_notify() and output_destroy_notify() contain logic to update the cursor and force a repaint when outputs are added or removed. This logic in fact needs to run on any output layout change, so consolidate it into a new function, output_update_for_layout_change(). Then add a second new function, view_adjust_for_layout_change(), which adjusts window placement to account for the new layout. The behavior is roughly as follows: - Normal windows that end up offscreen are centered on the closest output (making use of the existing view_center() logic) - Maximized windows are re-maximized on the closest output. Logic is also added to the unmaximize step to check that the original unmaximized position is still on-screen. - Fullscreen windows are re-fullscreened on the same output if possible; otherwise they are un-fullscreened. Minimized windows don't require any special handling. Their placement is adjusted just the same, but invisible to the user until they are later unminimized. There is some positioning glitch still with un-fullscreening a window whose output has been disconnected/disabled; it can end up in an unexpected position (but at least has the correct size and decoration). I don't think this is due to a bug in my change per se, but perhaps the change has exposed a bug elsewhere. Fixes: #177
2021-12-31 17:30:55 -05:00
}
}
static void
view_output_enter(struct view *view, struct wlr_output *wlr_output)
{
if (view->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_output_enter(
view->toplevel_handle, wlr_output);
}
}
static void
view_output_leave(struct view *view, struct wlr_output *wlr_output)
{
if (view->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_output_leave(
view->toplevel_handle, wlr_output);
}
}
/*
* At present, a view can only 'enter' one output at a time, although the view
* may span multiple outputs. Ideally we would handle multiple outputs, but
* this method is the simplest form of what we want.
*/
void
view_discover_output(struct view *view)
{
struct output *old_output = view->output;
struct output *new_output = view_output(view);
if (old_output != new_output) {
view->output = new_output;
view_output_enter(view, new_output->wlr_output);
if (old_output) {
view_output_leave(view, old_output->wlr_output);
}
}
}
void
2021-07-20 20:24:39 +01:00
view_move_to_edge(struct view *view, const char *direction)
{
if (!view) {
wlr_log(WLR_ERROR, "no view");
return;
}
struct output *output = view_output(view);
if (!output) {
wlr_log(WLR_ERROR, "no output");
return;
}
if (!direction) {
wlr_log(WLR_ERROR, "invalid edge");
return;
}
struct wlr_box usable = output_usable_area_in_layout_coords(output);
2022-04-04 20:53:36 +01:00
if (usable.height == output->wlr_output->height
&& output->wlr_output->scale != 1) {
usable.height /= output->wlr_output->scale;
}
2022-04-04 20:53:36 +01:00
if (usable.width == output->wlr_output->width
&& output->wlr_output->scale != 1) {
usable.width /= output->wlr_output->scale;
}
int x = 0, y = 0;
if (!strcasecmp(direction, "left")) {
x = usable.x + view->margin.left + rc.gap;
y = view->y;
} else if (!strcasecmp(direction, "up")) {
x = view->x;
y = usable.y + view->margin.top + rc.gap;
} else if (!strcasecmp(direction, "right")) {
x = usable.x + usable.width - view->w - view->margin.right
- rc.gap;
y = view->y;
} else if (!strcasecmp(direction, "down")) {
x = view->x;
y = usable.y + usable.height - view->h - view->margin.bottom
- rc.gap;
} else {
wlr_log(WLR_ERROR, "invalid edge");
return;
}
view_move(view, x, y);
}
enum view_edge {
VIEW_EDGE_INVALID,
VIEW_EDGE_LEFT,
VIEW_EDGE_RIGHT,
VIEW_EDGE_UP,
VIEW_EDGE_DOWN,
VIEW_EDGE_CENTER,
};
static enum view_edge
view_edge_invert(enum view_edge edge)
{
switch (edge) {
2021-11-26 18:49:44 +00:00
case VIEW_EDGE_LEFT:
return VIEW_EDGE_RIGHT;
case VIEW_EDGE_RIGHT:
return VIEW_EDGE_LEFT;
case VIEW_EDGE_UP:
return VIEW_EDGE_DOWN;
case VIEW_EDGE_DOWN:
return VIEW_EDGE_UP;
case VIEW_EDGE_CENTER:
case VIEW_EDGE_INVALID:
default:
return VIEW_EDGE_INVALID;
}
}
static enum view_edge
view_edge_parse(const char *direction)
{
if (!direction) {
return VIEW_EDGE_INVALID;
}
if (!strcasecmp(direction, "left")) {
return VIEW_EDGE_LEFT;
} else if (!strcasecmp(direction, "up")) {
return VIEW_EDGE_UP;
} else if (!strcasecmp(direction, "right")) {
return VIEW_EDGE_RIGHT;
} else if (!strcasecmp(direction, "down")) {
return VIEW_EDGE_DOWN;
2021-11-26 18:49:44 +00:00
} else if (!strcasecmp(direction, "center")) {
return VIEW_EDGE_CENTER;
} else {
return VIEW_EDGE_INVALID;
}
}
static struct wlr_box
2021-11-26 18:49:44 +00:00
view_get_edge_snap_box(struct view *view, struct output *output,
enum view_edge edge)
{
struct wlr_box usable = output_usable_area_in_layout_coords(output);
2022-04-04 20:53:36 +01:00
if (usable.height == output->wlr_output->height
&& output->wlr_output->scale != 1) {
usable.height /= output->wlr_output->scale;
}
2022-04-04 20:53:36 +01:00
if (usable.width == output->wlr_output->width
&& output->wlr_output->scale != 1) {
usable.width /= output->wlr_output->scale;
}
int x_offset = edge == VIEW_EDGE_RIGHT
? (usable.width + rc.gap) / 2 : rc.gap;
int y_offset = edge == VIEW_EDGE_DOWN
? (usable.height + rc.gap) / 2 : rc.gap;
int base_width, base_height;
switch (edge) {
case VIEW_EDGE_LEFT:
case VIEW_EDGE_RIGHT:
base_width = (usable.width - 3 * rc.gap) / 2;
base_height = usable.height - 2 * rc.gap;
break;
case VIEW_EDGE_UP:
case VIEW_EDGE_DOWN:
base_width = usable.width - 2 * rc.gap;
base_height = (usable.height - 3 * rc.gap) / 2;
break;
default:
case VIEW_EDGE_CENTER:
base_width = usable.width - 2 * rc.gap;
base_height = usable.height - 2 * rc.gap;
break;
}
struct wlr_box dst = {
.x = x_offset + usable.x + view->margin.left,
.y = y_offset + usable.y + view->margin.top,
.width = base_width - view->margin.left - view->margin.right,
.height = base_height - view->margin.top - view->margin.bottom,
};
return dst;
}
void
view_snap_to_edge(struct view *view, const char *direction)
{
if (!view) {
wlr_log(WLR_ERROR, "no view");
return;
}
struct output *output = view_output(view);
if (!output) {
wlr_log(WLR_ERROR, "no output");
return;
}
enum view_edge edge = view_edge_parse(direction);
if (edge == VIEW_EDGE_INVALID) {
wlr_log(WLR_ERROR, "invalid edge");
return;
}
struct wlr_box dst = view_get_edge_snap_box(view, output, edge);
2021-11-26 18:49:44 +00:00
if (view->x == dst.x && view->y == dst.y && view->w == dst.width
&& view->h == dst.height) {
/* Move over to the next screen if this is already snapped. */
2021-11-26 18:49:44 +00:00
struct wlr_box usable =
output_usable_area_in_layout_coords(output);
switch (edge) {
2021-11-26 18:49:44 +00:00
case VIEW_EDGE_LEFT:
dst.x -= (usable.width / 2) + 1;
break;
case VIEW_EDGE_RIGHT:
dst.x += (usable.width / 2) + 1;
break;
case VIEW_EDGE_UP:
dst.y -= (usable.height / 2) + 1;
break;
case VIEW_EDGE_DOWN:
dst.y += (usable.height / 2) + 1;
break;
default:
break;
}
2021-11-26 18:49:44 +00:00
struct wlr_output *new_wlr_output = wlr_output_layout_output_at(
view->server->output_layout, dst.x, dst.y);
struct output *new_output =
2021-11-26 18:49:44 +00:00
output_from_wlr_output(view->server, new_wlr_output);
2021-11-26 18:49:44 +00:00
if (new_output == output || !new_output
|| edge == VIEW_EDGE_CENTER) {
return;
}
2021-11-26 18:49:44 +00:00
dst = view_get_edge_snap_box(view, new_output,
view_edge_invert(edge));
}
if (view->w == dst.width && view->h == dst.height) {
/* move horizontally/vertically without changing size */
view_move(view, dst.x, dst.y);
} else {
view_move_resize(view, dst);
}
}
2021-10-18 20:01:10 +01:00
const char *
view_get_string_prop(struct view *view, const char *prop)
{
if (view->impl->get_string_prop) {
2021-12-06 20:16:30 +00:00
return view->impl->get_string_prop(view, prop);
2021-10-18 20:01:10 +01:00
}
2021-10-18 20:06:47 +01:00
return "";
2021-10-18 20:01:10 +01:00
}
void
view_update_title(struct view *view)
{
2021-10-18 20:01:10 +01:00
const char *title = view_get_string_prop(view, "title");
if (!view->toplevel_handle || !title) {
return;
}
ssd_update_title(view);
wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle, title);
}
void
view_update_app_id(struct view *view)
{
2021-10-18 20:01:10 +01:00
const char *app_id = view_get_string_prop(view, "app_id");
if (!view->toplevel_handle || !app_id) {
return;
}
wlr_foreign_toplevel_handle_v1_set_app_id(
view->toplevel_handle, app_id);
}
void
view_destroy(struct view *view)
{
if (view->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_destroy(view->toplevel_handle);
}
interactive_end(view);
if (view->server->seat.pressed.view == view) {
view->server->seat.pressed.view = NULL;
view->server->seat.pressed.surface = NULL;
}
if (view->server->cycle_view == view) {
/* If we are the current OSD selected view, cycle
* to the next because we are dying. */
view->server->cycle_view = desktop_cycle_view(view->server,
view->server->cycle_view, LAB_CYCLE_DIR_BACKWARD);
/* If we cycled back to ourselves, then we have no windows.
* just remove it and close the OSD for good. */
if (view->server->cycle_view == view || !view->server->cycle_view) {
view->server->cycle_view = NULL;
osd_finish(view->server);
}
}
if (view->server->cycle_view) {
/* Update the OSD to reflect the view has now gone. */
osd_update(view->server);
}
if (view->scene_tree) {
ssd_destroy(view);
wlr_scene_node_destroy(&view->scene_tree->node);
view->scene_tree = NULL;
}
/* Remove view from server->views */
wl_list_remove(&view->link);
free(view);
}