2021-09-24 21:45:48 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2023-09-03 19:15:50 +02:00
|
|
|
#include "input/keyboard.h"
|
2020-05-29 21:27:34 +01:00
|
|
|
#include "labwc.h"
|
2022-07-06 07:19:28 +02:00
|
|
|
#include "regions.h"
|
2023-08-17 18:59:29 +02:00
|
|
|
#include "resize_indicator.h"
|
2022-11-21 10:10:39 -05:00
|
|
|
#include "view.h"
|
2020-05-29 21:27:34 +01:00
|
|
|
|
2022-01-05 08:30:07 +01:00
|
|
|
static int
|
|
|
|
|
max_move_scale(double pos_cursor, double pos_current,
|
|
|
|
|
double size_current, double size_orig)
|
|
|
|
|
{
|
|
|
|
|
double anchor_frac = (pos_cursor - pos_current) / size_current;
|
|
|
|
|
int pos_new = pos_cursor - (size_orig * anchor_frac);
|
|
|
|
|
if (pos_new < pos_current) {
|
|
|
|
|
/* Clamp by using the old offsets of the maximized window */
|
|
|
|
|
pos_new = pos_current;
|
|
|
|
|
}
|
|
|
|
|
return pos_new;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
void
|
2020-10-21 20:30:06 +01:00
|
|
|
interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
|
2020-05-29 21:27:34 +01:00
|
|
|
{
|
2022-09-08 11:50:40 -04:00
|
|
|
/*
|
|
|
|
|
* This function sets up an interactive move or resize operation, where
|
|
|
|
|
* the compositor stops propagating pointer events to clients and
|
|
|
|
|
* instead consumes them itself, to move or resize windows.
|
|
|
|
|
*/
|
|
|
|
|
struct server *server = view->server;
|
2022-11-19 12:58:52 -05:00
|
|
|
struct seat *seat = &server->seat;
|
2023-02-08 23:19:14 -05:00
|
|
|
struct wlr_box geometry = view->current;
|
2022-11-19 12:58:52 -05:00
|
|
|
|
2023-03-07 22:59:56 +01:00
|
|
|
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-19 12:58:52 -05:00
|
|
|
switch (mode) {
|
|
|
|
|
case LAB_INPUT_STATE_MOVE:
|
|
|
|
|
if (view->fullscreen) {
|
|
|
|
|
/**
|
|
|
|
|
* We don't allow moving fullscreen windows.
|
|
|
|
|
*
|
|
|
|
|
* If you think there is a good reason to allow
|
|
|
|
|
* it, feel free to open an issue explaining
|
|
|
|
|
* your use-case.
|
|
|
|
|
*/
|
|
|
|
|
return;
|
|
|
|
|
}
|
view: implement separate horizontal/vertical maximize
This is a useful (if lesser-known) feature of at least a few popular X11
window managers, for example Openbox and XFWM4. Typically right-click on
the maximize button toggles horizontal maximize, while middle-click
toggles vertical maximize.
Support in labwc uses the same configuration syntax as Openbox, where the
Maximize/ToggleMaximize actions have an optional "direction" argument:
horizontal, vertical, or both (default). The default mouse bindings match
the XFWM4 defaults (not sure what Openbox has by default).
Most of the external protocols still assume "maximized" is a Boolean,
which is no longer true internally. For the sake of the outside world,
a view is only "maximized" if maximized in both directions.
Internally, I've taken the following approach:
- SSD code decorates the view as "maximized" (i.e. hiding borders) only
if maximized in both directions.
- Layout code (interactive move/resize, tiling, etc.) generally treats
the view as "maximized" (with the restrictions that entails) if
maximized in either direction. For example, moving a vertically-
maximized view first restores the natural geometry (this differs from
Openbox, which instead allows the view to move only horizontally.)
v2: use enum view_axis for view->maximized
v3:
- update docs
- allow resizing if partly maximized
- add TODOs & corrections noted by Consolatis
2023-10-26 00:38:29 -04:00
|
|
|
if (!view_is_floating(view)) {
|
2022-11-19 12:58:52 -05:00
|
|
|
/*
|
|
|
|
|
* Un-maximize and restore natural width/height.
|
|
|
|
|
* Don't reset tiled state yet since we may want
|
|
|
|
|
* to keep it (in the snap-to-maximize case).
|
|
|
|
|
*/
|
|
|
|
|
geometry = view->natural_geometry;
|
2023-02-08 23:19:14 -05:00
|
|
|
geometry.x = max_move_scale(seat->cursor->x,
|
|
|
|
|
view->current.x, view->current.width,
|
|
|
|
|
geometry.width);
|
|
|
|
|
geometry.y = max_move_scale(seat->cursor->y,
|
|
|
|
|
view->current.y, view->current.height,
|
|
|
|
|
geometry.height);
|
2022-11-19 12:58:52 -05:00
|
|
|
view_restore_to(view, geometry);
|
|
|
|
|
} else {
|
|
|
|
|
/* Store natural geometry at start of move */
|
|
|
|
|
view_store_natural_geometry(view);
|
|
|
|
|
}
|
2023-03-07 17:51:28 +01:00
|
|
|
|
|
|
|
|
/* Prevent region snapping when just moving via A-Left mousebind */
|
|
|
|
|
struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard;
|
|
|
|
|
seat->region_prevent_snap = keyboard_any_modifiers_pressed(keyboard);
|
|
|
|
|
|
2022-11-19 12:58:52 -05:00
|
|
|
cursor_set(seat, LAB_CURSOR_GRAB);
|
|
|
|
|
break;
|
|
|
|
|
case LAB_INPUT_STATE_RESIZE:
|
view: implement separate horizontal/vertical maximize
This is a useful (if lesser-known) feature of at least a few popular X11
window managers, for example Openbox and XFWM4. Typically right-click on
the maximize button toggles horizontal maximize, while middle-click
toggles vertical maximize.
Support in labwc uses the same configuration syntax as Openbox, where the
Maximize/ToggleMaximize actions have an optional "direction" argument:
horizontal, vertical, or both (default). The default mouse bindings match
the XFWM4 defaults (not sure what Openbox has by default).
Most of the external protocols still assume "maximized" is a Boolean,
which is no longer true internally. For the sake of the outside world,
a view is only "maximized" if maximized in both directions.
Internally, I've taken the following approach:
- SSD code decorates the view as "maximized" (i.e. hiding borders) only
if maximized in both directions.
- Layout code (interactive move/resize, tiling, etc.) generally treats
the view as "maximized" (with the restrictions that entails) if
maximized in either direction. For example, moving a vertically-
maximized view first restores the natural geometry (this differs from
Openbox, which instead allows the view to move only horizontally.)
v2: use enum view_axis for view->maximized
v3:
- update docs
- allow resizing if partly maximized
- add TODOs & corrections noted by Consolatis
2023-10-26 00:38:29 -04:00
|
|
|
if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) {
|
2022-11-19 12:58:52 -05:00
|
|
|
/*
|
view: implement separate horizontal/vertical maximize
This is a useful (if lesser-known) feature of at least a few popular X11
window managers, for example Openbox and XFWM4. Typically right-click on
the maximize button toggles horizontal maximize, while middle-click
toggles vertical maximize.
Support in labwc uses the same configuration syntax as Openbox, where the
Maximize/ToggleMaximize actions have an optional "direction" argument:
horizontal, vertical, or both (default). The default mouse bindings match
the XFWM4 defaults (not sure what Openbox has by default).
Most of the external protocols still assume "maximized" is a Boolean,
which is no longer true internally. For the sake of the outside world,
a view is only "maximized" if maximized in both directions.
Internally, I've taken the following approach:
- SSD code decorates the view as "maximized" (i.e. hiding borders) only
if maximized in both directions.
- Layout code (interactive move/resize, tiling, etc.) generally treats
the view as "maximized" (with the restrictions that entails) if
maximized in either direction. For example, moving a vertically-
maximized view first restores the natural geometry (this differs from
Openbox, which instead allows the view to move only horizontally.)
v2: use enum view_axis for view->maximized
v3:
- update docs
- allow resizing if partly maximized
- add TODOs & corrections noted by Consolatis
2023-10-26 00:38:29 -04:00
|
|
|
* We don't allow resizing while fullscreen or
|
|
|
|
|
* maximized in both directions.
|
2022-11-19 12:58:52 -05:00
|
|
|
*/
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/*
|
view: implement separate horizontal/vertical maximize
This is a useful (if lesser-known) feature of at least a few popular X11
window managers, for example Openbox and XFWM4. Typically right-click on
the maximize button toggles horizontal maximize, while middle-click
toggles vertical maximize.
Support in labwc uses the same configuration syntax as Openbox, where the
Maximize/ToggleMaximize actions have an optional "direction" argument:
horizontal, vertical, or both (default). The default mouse bindings match
the XFWM4 defaults (not sure what Openbox has by default).
Most of the external protocols still assume "maximized" is a Boolean,
which is no longer true internally. For the sake of the outside world,
a view is only "maximized" if maximized in both directions.
Internally, I've taken the following approach:
- SSD code decorates the view as "maximized" (i.e. hiding borders) only
if maximized in both directions.
- Layout code (interactive move/resize, tiling, etc.) generally treats
the view as "maximized" (with the restrictions that entails) if
maximized in either direction. For example, moving a vertically-
maximized view first restores the natural geometry (this differs from
Openbox, which instead allows the view to move only horizontally.)
v2: use enum view_axis for view->maximized
v3:
- update docs
- allow resizing if partly maximized
- add TODOs & corrections noted by Consolatis
2023-10-26 00:38:29 -04:00
|
|
|
* If tiled or maximized in only one direction, reset
|
|
|
|
|
* tiled/maximized state but keep the same geometry as
|
|
|
|
|
* the starting point for the resize.
|
2022-11-19 12:58:52 -05:00
|
|
|
*/
|
2022-11-21 13:17:14 -05:00
|
|
|
view_set_untiled(view);
|
view: implement separate horizontal/vertical maximize
This is a useful (if lesser-known) feature of at least a few popular X11
window managers, for example Openbox and XFWM4. Typically right-click on
the maximize button toggles horizontal maximize, while middle-click
toggles vertical maximize.
Support in labwc uses the same configuration syntax as Openbox, where the
Maximize/ToggleMaximize actions have an optional "direction" argument:
horizontal, vertical, or both (default). The default mouse bindings match
the XFWM4 defaults (not sure what Openbox has by default).
Most of the external protocols still assume "maximized" is a Boolean,
which is no longer true internally. For the sake of the outside world,
a view is only "maximized" if maximized in both directions.
Internally, I've taken the following approach:
- SSD code decorates the view as "maximized" (i.e. hiding borders) only
if maximized in both directions.
- Layout code (interactive move/resize, tiling, etc.) generally treats
the view as "maximized" (with the restrictions that entails) if
maximized in either direction. For example, moving a vertically-
maximized view first restores the natural geometry (this differs from
Openbox, which instead allows the view to move only horizontally.)
v2: use enum view_axis for view->maximized
v3:
- update docs
- allow resizing if partly maximized
- add TODOs & corrections noted by Consolatis
2023-10-26 00:38:29 -04:00
|
|
|
view_restore_to(view, view->pending);
|
2022-11-19 12:58:52 -05:00
|
|
|
cursor_set(seat, cursor_get_from_edge(edges));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* Should not be reached */
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-09-08 11:50:40 -04:00
|
|
|
|
2022-11-19 12:58:52 -05:00
|
|
|
server->input_mode = mode;
|
|
|
|
|
server->grabbed_view = view;
|
2022-09-08 11:50:40 -04:00
|
|
|
/* Remember view and cursor positions at start of move/resize */
|
|
|
|
|
server->grab_x = seat->cursor->x;
|
|
|
|
|
server->grab_y = seat->cursor->y;
|
2022-11-19 12:58:52 -05:00
|
|
|
server->grab_box = geometry;
|
2022-09-08 11:50:40 -04:00
|
|
|
server->resize_edges = edges;
|
2023-08-17 18:59:29 +02:00
|
|
|
if (rc.resize_indicator) {
|
|
|
|
|
resize_indicator_show(view);
|
|
|
|
|
}
|
2022-11-19 12:58:52 -05:00
|
|
|
}
|
2022-09-08 11:50:40 -04:00
|
|
|
|
2022-11-19 12:58:52 -05:00
|
|
|
/* Returns true if view was snapped to any edge */
|
|
|
|
|
static bool
|
|
|
|
|
snap_to_edge(struct view *view)
|
|
|
|
|
{
|
|
|
|
|
int snap_range = rc.snap_edge_range;
|
|
|
|
|
if (!snap_range) {
|
|
|
|
|
return false;
|
2021-03-06 18:34:52 +00:00
|
|
|
}
|
2022-07-01 20:42:41 +02:00
|
|
|
|
2022-11-19 12:58:52 -05:00
|
|
|
/* Translate into output local coordinates */
|
|
|
|
|
double cursor_x = view->server->seat.cursor->x;
|
|
|
|
|
double cursor_y = view->server->seat.cursor->y;
|
|
|
|
|
wlr_output_layout_output_coords(view->server->output_layout,
|
|
|
|
|
view->output->wlr_output, &cursor_x, &cursor_y);
|
2021-03-06 18:34:52 +00:00
|
|
|
|
2022-11-19 12:58:52 -05:00
|
|
|
/*
|
|
|
|
|
* Don't store natural geometry here (it was
|
|
|
|
|
* stored already in interactive_begin())
|
|
|
|
|
*/
|
|
|
|
|
struct wlr_box *area = &view->output->usable_area;
|
|
|
|
|
if (cursor_x <= area->x + snap_range) {
|
2023-08-02 04:30:50 +02:00
|
|
|
view_snap_to_edge(view, VIEW_EDGE_LEFT,
|
2022-11-19 12:58:52 -05:00
|
|
|
/*store_natural_geometry*/ false);
|
|
|
|
|
} else if (cursor_x >= area->x + area->width - snap_range) {
|
2023-08-02 04:30:50 +02:00
|
|
|
view_snap_to_edge(view, VIEW_EDGE_RIGHT,
|
2022-11-19 12:58:52 -05:00
|
|
|
/*store_natural_geometry*/ false);
|
|
|
|
|
} else if (cursor_y <= area->y + snap_range) {
|
|
|
|
|
if (rc.snap_top_maximize) {
|
view: implement separate horizontal/vertical maximize
This is a useful (if lesser-known) feature of at least a few popular X11
window managers, for example Openbox and XFWM4. Typically right-click on
the maximize button toggles horizontal maximize, while middle-click
toggles vertical maximize.
Support in labwc uses the same configuration syntax as Openbox, where the
Maximize/ToggleMaximize actions have an optional "direction" argument:
horizontal, vertical, or both (default). The default mouse bindings match
the XFWM4 defaults (not sure what Openbox has by default).
Most of the external protocols still assume "maximized" is a Boolean,
which is no longer true internally. For the sake of the outside world,
a view is only "maximized" if maximized in both directions.
Internally, I've taken the following approach:
- SSD code decorates the view as "maximized" (i.e. hiding borders) only
if maximized in both directions.
- Layout code (interactive move/resize, tiling, etc.) generally treats
the view as "maximized" (with the restrictions that entails) if
maximized in either direction. For example, moving a vertically-
maximized view first restores the natural geometry (this differs from
Openbox, which instead allows the view to move only horizontally.)
v2: use enum view_axis for view->maximized
v3:
- update docs
- allow resizing if partly maximized
- add TODOs & corrections noted by Consolatis
2023-10-26 00:38:29 -04:00
|
|
|
view_maximize(view, VIEW_AXIS_BOTH,
|
2022-11-19 12:58:52 -05:00
|
|
|
/*store_natural_geometry*/ false);
|
|
|
|
|
} else {
|
2023-08-02 04:30:50 +02:00
|
|
|
view_snap_to_edge(view, VIEW_EDGE_UP,
|
2022-11-19 12:58:52 -05:00
|
|
|
/*store_natural_geometry*/ false);
|
|
|
|
|
}
|
|
|
|
|
} else if (cursor_y >= area->y + area->height - snap_range) {
|
2023-08-02 04:30:50 +02:00
|
|
|
view_snap_to_edge(view, VIEW_EDGE_DOWN,
|
2022-11-19 12:58:52 -05:00
|
|
|
/*store_natural_geometry*/ false);
|
|
|
|
|
} else {
|
|
|
|
|
/* Not close to any edge */
|
|
|
|
|
return false;
|
2021-11-28 21:47:24 +00:00
|
|
|
}
|
2022-11-19 12:58:52 -05:00
|
|
|
|
|
|
|
|
return true;
|
2020-05-29 21:27:34 +01:00
|
|
|
}
|
2021-12-03 08:09:19 +00:00
|
|
|
|
2022-07-06 17:05:34 +02:00
|
|
|
static bool
|
|
|
|
|
snap_to_region(struct view *view)
|
|
|
|
|
{
|
2023-03-07 17:51:28 +01:00
|
|
|
if (!regions_should_snap(view->server)) {
|
2022-07-06 17:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 17:51:28 +01:00
|
|
|
struct region *region = regions_from_cursor(view->server);
|
|
|
|
|
if (region) {
|
|
|
|
|
view_snap_to_region(view, region,
|
|
|
|
|
/*store_natural_geometry*/ false);
|
|
|
|
|
return true;
|
2022-07-06 17:05:34 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 23:15:28 +00:00
|
|
|
void
|
2022-11-19 12:58:52 -05:00
|
|
|
interactive_finish(struct view *view)
|
2021-12-03 23:15:28 +00:00
|
|
|
{
|
2021-12-03 08:09:19 +00:00
|
|
|
if (view->server->grabbed_view == view) {
|
2022-09-13 08:15:33 +02:00
|
|
|
regions_hide_overlay(&view->server->seat);
|
2023-03-07 17:51:28 +01:00
|
|
|
if (view->server->input_mode == LAB_INPUT_STATE_MOVE) {
|
2022-07-06 17:05:34 +02:00
|
|
|
if (!snap_to_region(view)) {
|
|
|
|
|
if (!snap_to_edge(view)) {
|
|
|
|
|
/* Reset tiled state if not snapped */
|
|
|
|
|
view_set_untiled(view);
|
|
|
|
|
}
|
2022-01-08 11:25:18 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-08-17 18:59:29 +02:00
|
|
|
resize_indicator_hide(view);
|
2023-03-07 17:51:28 +01:00
|
|
|
|
|
|
|
|
view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
|
|
|
|
|
view->server->grabbed_view = NULL;
|
|
|
|
|
|
2022-09-12 13:14:18 -04:00
|
|
|
/* Update focus/cursor image */
|
|
|
|
|
cursor_update_focus(view->server);
|
2021-12-03 08:09:19 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-19 12:58:52 -05:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Cancels interative move/resize without changing the state of the of
|
|
|
|
|
* the view in any way. This may leave the tiled state inconsistent with
|
|
|
|
|
* the actual geometry of the view.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
interactive_cancel(struct view *view)
|
|
|
|
|
{
|
|
|
|
|
if (view->server->grabbed_view == view) {
|
2023-08-17 18:59:29 +02:00
|
|
|
resize_indicator_hide(view);
|
2022-11-19 12:58:52 -05:00
|
|
|
view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
|
|
|
|
|
view->server->grabbed_view = NULL;
|
|
|
|
|
/* Update focus/cursor image */
|
|
|
|
|
cursor_update_focus(view->server);
|
|
|
|
|
}
|
|
|
|
|
}
|