Merge branch 'labwc:master' into adaptive_sync_fs

This commit is contained in:
Ph42oN 2024-01-08 12:34:20 +02:00 committed by GitHub
commit bfece419bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 233 additions and 45 deletions

View file

@ -407,6 +407,11 @@ windows using the mouse.
- Left
- Middle
- Right
- Side
- Extra
- Forward
- Back
- Task
Supported scroll *directions* are:
- Up
@ -433,6 +438,57 @@ windows using the mouse.
<mouse><mousebind> entries exist, the same default mousebinds will be
loaded even if the <default /> element is not provided.
## TABLET
```
<tablet rotate="0">
<area top="0.0" left="0.0" width="0.0" height="0.0">
<map button="Tip" to="Left" />
<map button="Stylus" to="Right" />
<map button="Stylus2" to="Middle" />
</tablet>
```
*<tablet><area top="mm" left="mm" width="mm" height="mm" />*
By default the complete tablet area is mapped to the full output.
The *area* element can be used to truncate the active area of the
tablet surface. By truncating the active area, it is e.g. possible
to maintain the same aspect ratio between output and tablet.
The active tablet area can be specified by setting the *top*/*left*
coordinate (in mm) and/or *width*/*height* (in mm). If width or
height are omitted or default (0.0), width/height will be set to
the remaining width/height seen from top/left.
Aspect ratio example:
The dimensions of the tablet are 215mm x 115mm and the output has
a resolution of 3440x1440. When setting height to "90", because
215 x 1440 / 3440 = 90, the responsive tablet area height will be
truncated to match the 21:9 aspect ratio of the output. By
additionally setting top to "12.5", the active area is centered
vertically on the tablet surface.
*<tablet rotate="" />* [0|90|180|270]
The tablet orientation can be changed in 90 degree steps. Default is
no rotation (0). Rotation will be applied after applying tablet area
transformation.
*<tablet><map button="" to="" />*
Tablet buttons emulate regular mouse buttons. If not specified otherwise,
the tip (Tip) is mapped to left mouse click, the first pen button (Stylus)
is mapped to right mouse button click and the second pen button (Stylus2)
emulates a middle mouse button click.
Supported map *buttons* are:
- Tip
- Stylus
- Stylus2
- Stylus3
- Pad
- Pad2..Pad9
See mouse section above for all supported *to* mouse buttons.
## LIBINPUT
```

View file

@ -394,25 +394,25 @@
</mouse>
<!--
The active tablet area can be specified by setting the top/left
coordinate (in mm) and/or width/height (in mm). If width or height
are omitted or default (0.0), width/height will be set to the
remaining width/height seen from top/left.
The active tablet area can be specified by setting the *top*/*left*
coordinate (in mm) and/or *width*/*height* (in mm). If width or
height are omitted or default (0.0), width/height will be set to
the remaining width/height seen from top/left.
The tablet orientation can be changed in 90 degree steps, thus
rotation can be set to [0|90|180|270]. Rotation will be applied
*rotate* can be set to [0|90|180|270]. Rotation will be applied
after applying tablet area transformation.
Tablet buttons emulate regular mouse buttons. The tablet *button* can
be set to any of [tip|stylus|stylus2|stylus3|pad|pad2|pad3|..|pad9].
Valid *to* mouse buttons are [left|right|middle].
be set to any of [Tip|Stylus|Stylus2|Stylus3|Pad|Pad2|Pad3|..|Pad9].
Valid *to* mouse buttons are [Left|Right|Middle].
-->
<tablet rotate="0">
<!-- Active area dimensions are in mm -->
<area top="0.0" left="0.0" width="0.0" height="0.0">
<map button="tip" to="left" />
<map button="stylus" to="right" />
<map button="stylus2" to="middle" />
<map button="Tip" to="Left" />
<map button="Stylus" to="Right" />
<map button="Stylus2" to="Middle" />
</tablet>
<!--

View file

@ -20,7 +20,6 @@ struct button_map_entry {
double tablet_get_dbl_if_positive(const char *content, const char *name);
enum rotation tablet_parse_rotation(int value);
uint32_t tablet_button_from_str(const char *button);
uint32_t mouse_button_from_str(const char *button);
void tablet_button_mapping_add(uint32_t from, uint32_t to);
void tablet_load_default_button_mappings(void);

View file

@ -180,6 +180,13 @@ struct view {
* maximized/fullscreen/tiled.
*/
struct wlr_box natural_geometry;
/*
* Whenever an output layout change triggers a view relocation, the
* last pending position (or natural geometry) will be saved so the
* view may be restored to its original location on a subsequent layout
* change.
*/
struct wlr_box last_layout_geometry;
/* used by xdg-shell views */
uint32_t pending_configure_serial;
@ -420,6 +427,7 @@ bool view_is_floating(struct view *view);
void view_move_to_workspace(struct view *view, struct workspace *workspace);
void view_set_decorations(struct view *view, bool decorations);
void view_toggle_fullscreen(struct view *view);
void view_invalidate_last_layout_geometry(struct view *view);
void view_adjust_for_layout_change(struct view *view);
void view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows);
void view_grow_to_edge(struct view *view, enum view_edge direction);

View file

@ -34,6 +34,16 @@ mousebind_button_from_str(const char *str, uint32_t *modifiers)
return BTN_RIGHT;
} else if (!strcasecmp(str, "Middle")) {
return BTN_MIDDLE;
} else if (!strcasecmp(str, "Side")) {
return BTN_SIDE;
} else if (!strcasecmp(str, "Extra")) {
return BTN_EXTRA;
} else if (!strcasecmp(str, "Forward")) {
return BTN_FORWARD;
} else if (!strcasecmp(str, "Back")) {
return BTN_BACK;
} else if (!strcasecmp(str, "Task")) {
return BTN_TASK;
}
invalid:
wlr_log(WLR_ERROR, "unknown button (%s)", str);

View file

@ -868,7 +868,7 @@ entry(xmlNode *node, char *nodename, char *content)
button_map_from = tablet_button_from_str(content);
} else if (!strcasecmp(nodename, "to.map.tablet")) {
if (button_map_from != UINT32_MAX) {
uint32_t button_map_to = mouse_button_from_str(content);
uint32_t button_map_to = mousebind_button_from_str(content, NULL);
if (button_map_to != UINT32_MAX) {
tablet_button_mapping_add(button_map_from, button_map_to);
}

View file

@ -72,20 +72,6 @@ tablet_button_from_str(const char *button)
return UINT32_MAX;
}
uint32_t
mouse_button_from_str(const char *button)
{
if (!strcasecmp(button, "Left")) {
return BTN_LEFT;
} else if (!strcasecmp(button, "Right")) {
return BTN_RIGHT;
} else if (!strcasecmp(button, "Middle")) {
return BTN_MIDDLE;
}
wlr_log(WLR_ERROR, "Invalid value for mouse button: %s", button);
return UINT32_MAX;
}
void
tablet_button_mapping_add(uint32_t from, uint32_t to)
{

View file

@ -70,6 +70,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
} else {
/* Store natural geometry at start of move */
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
/* Prevent region snapping when just moving via A-Left mousebind */
@ -86,6 +87,13 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
*/
return;
}
/*
* Resizing overrides any attempt to restore window
* geometries altered by layout changes.
*/
view_invalidate_last_layout_geometry(view);
/*
* If tiled or maximized in only one direction, reset
* tiled/maximized state but keep the same geometry as

View file

@ -252,14 +252,27 @@ view_get_edge_snap_box(struct view *view, struct output *output,
return dst;
}
static void
view_discover_output(struct view *view)
static bool
view_discover_output(struct view *view, struct wlr_box *geometry)
{
assert(view);
assert(!view->fullscreen);
view->output = output_nearest_to(view->server,
view->current.x + view->current.width / 2,
view->current.y + view->current.height / 2);
if (!geometry) {
geometry = &view->current;
}
struct output *output =
output_nearest_to(view->server,
geometry->x + geometry->width / 2,
geometry->y + geometry->height / 2);
if (output && output != view->output) {
view->output = output;
return true;
}
return false;
}
static void
@ -343,7 +356,7 @@ view_moved(struct view *view)
* output when they enter that state.
*/
if (view_is_floating(view)) {
view_discover_output(view);
view_discover_output(view, NULL);
}
ssd_update_geometry(view->ssd);
cursor_update_focus(view->server);
@ -614,11 +627,11 @@ view_adjust_floating_geometry(struct view *view, struct wlr_box *geometry)
bool adjusted = false;
/*
* First check whether the view is onscreen. For now, "onscreen"
* is defined as even one pixel of the client area being visible.
* First check whether the view is the target screen, meaning that at
* least one client pixel is on the screen.
*/
if (wlr_output_layout_intersects(view->server->output_layout,
NULL, geometry)) {
view->output->wlr_output, geometry)) {
/*
* If onscreen, then make sure the titlebar is also
* visible (and not overlapping any panels/docks)
@ -988,6 +1001,7 @@ view_maximize(struct view *view, enum view_axis axis,
interactive_cancel(view);
if (store_natural_geometry && view_is_floating(view)) {
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
}
set_maximized(view, axis);
@ -1200,6 +1214,7 @@ view_set_fullscreen(struct view *view, bool fullscreen)
*/
interactive_cancel(view);
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
set_fullscreen(view, fullscreen);
@ -1211,37 +1226,141 @@ view_set_fullscreen(struct view *view, bool fullscreen)
set_adaptive_sync_fullscreen(view);
}
static bool
last_layout_geometry_is_valid(struct view *view)
{
return view->last_layout_geometry.width > 0
&& view->last_layout_geometry.height > 0;
}
static void
update_last_layout_geometry(struct view *view)
{
/*
* Only update an invalid last-layout geometry to prevent a series of
* successive layout changes from continually replacing the "preferred"
* location with whatever location the view currently holds. The
* "preferred" location should be whatever state was set by user
* interaction, not automatic responses to layout changes.
*/
if (last_layout_geometry_is_valid(view)) {
return;
}
if (view_is_floating(view)) {
view->last_layout_geometry = view->pending;
} else {
view->last_layout_geometry = view->natural_geometry;
}
}
static bool
apply_last_layout_geometry(struct view *view, bool force_update)
{
/* Only apply a valid last-layout geometry */
if (!last_layout_geometry_is_valid(view)) {
return false;
}
/*
* Unless forced, the last-layout geometry is only applied
* when the relevant view geometry is distinct.
*/
if (!force_update) {
struct wlr_box *relevant = view_is_floating(view) ?
&view->pending : &view->natural_geometry;
if (wlr_box_equal(relevant, &view->last_layout_geometry)) {
return false;
}
}
view->natural_geometry = view->last_layout_geometry;
view_adjust_floating_geometry(view, &view->natural_geometry);
return true;
}
void
view_invalidate_last_layout_geometry(struct view *view)
{
assert(view);
view->last_layout_geometry.width = 0;
view->last_layout_geometry.height = 0;
}
void
view_adjust_for_layout_change(struct view *view)
{
assert(view);
/* Exit fullscreen if output is lost */
bool was_fullscreen = view->fullscreen;
if (was_fullscreen && !output_is_usable(view->output)) {
set_fullscreen(view, false);
bool is_floating = view_is_floating(view);
if (!output_is_usable(view->output)) {
/* A view losing an output should have a last-layout geometry */
update_last_layout_geometry(view);
/* Exit fullscreen and re-assess floating status */
if (was_fullscreen) {
set_fullscreen(view, false);
is_floating = view_is_floating(view);
}
}
/* Restore any full-screen window to natural geometry */
bool use_natural = was_fullscreen;
/* Capture a pointer to the last-layout geometry (only if valid) */
struct wlr_box *last_geometry = NULL;
if (last_layout_geometry_is_valid(view)) {
last_geometry = &view->last_layout_geometry;
}
/*
* Floating views are always assigned to the nearest output.
* Maximized/tiled views remain on the same output if possible.
* Check if an output change is required:
* - Floating views are always mapped to the nearest output
* - Any view without a usable output needs to be repositioned
* - Any view with a valid last-layout geometry might be better
* positioned on another output
*/
bool is_floating = view_is_floating(view);
if (is_floating || !output_is_usable(view->output)) {
view_discover_output(view);
if (is_floating || last_geometry || !output_is_usable(view->output)) {
/* Move the view to an appropriate output, if needed */
bool output_changed = view_discover_output(view, last_geometry);
/*
* Try to apply the last-layout to the natural geometry
* (adjusting to ensure that it fits on the screen). This is
* forced if the output has changed, but will be done
* opportunistically even on the same output if the last-layout
* geometry is different from the view's governing geometry.
*/
if (apply_last_layout_geometry(view, output_changed)) {
use_natural = true;
}
/*
* Whether or not the view has moved, the layout has changed.
* Ensure that the view now has a valid last-layout geometry.
*/
update_last_layout_geometry(view);
}
if (!is_floating) {
view_apply_special_geometry(view);
} else if (was_fullscreen) {
} else if (use_natural) {
/*
* Move the window to its natural location, either because it
* was fullscreen or we are trying to restore a prior layout.
*/
view_apply_natural_geometry(view);
} else {
/* reposition view if it's offscreen */
/* Otherwise, just ensure the view is on screen. */
struct wlr_box geometry = view->pending;
if (view_adjust_floating_geometry(view, &geometry)) {
view_move_resize(view, geometry);
}
}
if (view->toplevel.handle) {
foreign_toplevel_update_outputs(view);
}
@ -1542,6 +1661,7 @@ view_snap_to_edge(struct view *view, enum view_edge edge,
} else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
view_set_untiled(view);
view_set_output(view, output);
@ -1571,6 +1691,7 @@ view_snap_to_region(struct view *view, struct region *region,
} else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
view_set_untiled(view);
view->tiled_region = region;