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 - Left
- Middle - Middle
- Right - Right
- Side
- Extra
- Forward
- Back
- Task
Supported scroll *directions* are: Supported scroll *directions* are:
- Up - Up
@ -433,6 +438,57 @@ windows using the mouse.
<mouse><mousebind> entries exist, the same default mousebinds will be <mouse><mousebind> entries exist, the same default mousebinds will be
loaded even if the <default /> element is not provided. 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 ## LIBINPUT
``` ```

View file

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

View file

@ -20,7 +20,6 @@ struct button_map_entry {
double tablet_get_dbl_if_positive(const char *content, const char *name); double tablet_get_dbl_if_positive(const char *content, const char *name);
enum rotation tablet_parse_rotation(int value); enum rotation tablet_parse_rotation(int value);
uint32_t tablet_button_from_str(const char *button); 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_button_mapping_add(uint32_t from, uint32_t to);
void tablet_load_default_button_mappings(void); void tablet_load_default_button_mappings(void);

View file

@ -180,6 +180,13 @@ struct view {
* maximized/fullscreen/tiled. * maximized/fullscreen/tiled.
*/ */
struct wlr_box natural_geometry; 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 */ /* used by xdg-shell views */
uint32_t pending_configure_serial; 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_move_to_workspace(struct view *view, struct workspace *workspace);
void view_set_decorations(struct view *view, bool decorations); void view_set_decorations(struct view *view, bool decorations);
void view_toggle_fullscreen(struct view *view); 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_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_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); 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; return BTN_RIGHT;
} else if (!strcasecmp(str, "Middle")) { } else if (!strcasecmp(str, "Middle")) {
return BTN_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: invalid:
wlr_log(WLR_ERROR, "unknown button (%s)", str); 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); button_map_from = tablet_button_from_str(content);
} else if (!strcasecmp(nodename, "to.map.tablet")) { } else if (!strcasecmp(nodename, "to.map.tablet")) {
if (button_map_from != UINT32_MAX) { 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) { if (button_map_to != UINT32_MAX) {
tablet_button_mapping_add(button_map_from, button_map_to); 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; 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 void
tablet_button_mapping_add(uint32_t from, uint32_t to) 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 { } else {
/* Store natural geometry at start of move */ /* Store natural geometry at start of move */
view_store_natural_geometry(view); view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
} }
/* Prevent region snapping when just moving via A-Left mousebind */ /* 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; 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 * If tiled or maximized in only one direction, reset
* tiled/maximized state but keep the same geometry as * 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; return dst;
} }
static void static bool
view_discover_output(struct view *view) view_discover_output(struct view *view, struct wlr_box *geometry)
{ {
assert(view); assert(view);
assert(!view->fullscreen); assert(!view->fullscreen);
view->output = output_nearest_to(view->server,
view->current.x + view->current.width / 2, if (!geometry) {
view->current.y + view->current.height / 2); 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 static void
@ -343,7 +356,7 @@ view_moved(struct view *view)
* output when they enter that state. * output when they enter that state.
*/ */
if (view_is_floating(view)) { if (view_is_floating(view)) {
view_discover_output(view); view_discover_output(view, NULL);
} }
ssd_update_geometry(view->ssd); ssd_update_geometry(view->ssd);
cursor_update_focus(view->server); cursor_update_focus(view->server);
@ -614,11 +627,11 @@ view_adjust_floating_geometry(struct view *view, struct wlr_box *geometry)
bool adjusted = false; bool adjusted = false;
/* /*
* First check whether the view is onscreen. For now, "onscreen" * First check whether the view is the target screen, meaning that at
* is defined as even one pixel of the client area being visible. * least one client pixel is on the screen.
*/ */
if (wlr_output_layout_intersects(view->server->output_layout, 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 * If onscreen, then make sure the titlebar is also
* visible (and not overlapping any panels/docks) * visible (and not overlapping any panels/docks)
@ -988,6 +1001,7 @@ view_maximize(struct view *view, enum view_axis axis,
interactive_cancel(view); interactive_cancel(view);
if (store_natural_geometry && view_is_floating(view)) { if (store_natural_geometry && view_is_floating(view)) {
view_store_natural_geometry(view); view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
} }
} }
set_maximized(view, axis); set_maximized(view, axis);
@ -1200,6 +1214,7 @@ view_set_fullscreen(struct view *view, bool fullscreen)
*/ */
interactive_cancel(view); interactive_cancel(view);
view_store_natural_geometry(view); view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
} }
set_fullscreen(view, fullscreen); set_fullscreen(view, fullscreen);
@ -1211,37 +1226,141 @@ view_set_fullscreen(struct view *view, bool fullscreen)
set_adaptive_sync_fullscreen(view); 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 void
view_adjust_for_layout_change(struct view *view) view_adjust_for_layout_change(struct view *view)
{ {
assert(view); assert(view);
/* Exit fullscreen if output is lost */
bool was_fullscreen = view->fullscreen; bool was_fullscreen = view->fullscreen;
if (was_fullscreen && !output_is_usable(view->output)) { bool is_floating = view_is_floating(view);
set_fullscreen(view, false);
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. * Check if an output change is required:
* Maximized/tiled views remain on the same output if possible. * - 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 || last_geometry || !output_is_usable(view->output)) {
if (is_floating || !output_is_usable(view->output)) { /* Move the view to an appropriate output, if needed */
view_discover_output(view); 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) { if (!is_floating) {
view_apply_special_geometry(view); 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); view_apply_natural_geometry(view);
} else { } else {
/* reposition view if it's offscreen */ /* Otherwise, just ensure the view is on screen. */
struct wlr_box geometry = view->pending; struct wlr_box geometry = view->pending;
if (view_adjust_floating_geometry(view, &geometry)) { if (view_adjust_floating_geometry(view, &geometry)) {
view_move_resize(view, geometry); view_move_resize(view, geometry);
} }
} }
if (view->toplevel.handle) { if (view->toplevel.handle) {
foreign_toplevel_update_outputs(view); 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) { } else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */ /* store current geometry as new natural_geometry */
view_store_natural_geometry(view); view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
} }
view_set_untiled(view); view_set_untiled(view);
view_set_output(view, output); view_set_output(view, output);
@ -1571,6 +1691,7 @@ view_snap_to_region(struct view *view, struct region *region,
} else if (store_natural_geometry) { } else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */ /* store current geometry as new natural_geometry */
view_store_natural_geometry(view); view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
} }
view_set_untiled(view); view_set_untiled(view);
view->tiled_region = region; view->tiled_region = region;