Merge branch 'master' into headerbar_touch

This commit is contained in:
Simon Long 2024-03-05 09:30:04 +00:00
commit 861c0de99f
43 changed files with 1128 additions and 181 deletions

View file

@ -98,7 +98,7 @@ Some distributions carry labwc in their repositories or user repositories.
- @adcdam (Slackware)
- @bdantas (Tiny Core Linux)
- @Visone-Selektah (Venom Linux)
- @tranzystorek-io (Void Linux)
- @tranzystorekk (Void Linux)
kindly maintain the packages in their respective distro.
@ -350,19 +350,32 @@ Base both bugfixes and new features on `master`.
# Native Language Support
## Translators
### Weblate Instance
Translators can create an account at [LXQt Weblate](https://translate.lxqt-project.org/projects/labwc/labwc/)
and use the web interface. Adding new languages should work, otherwise the
administrators can be contacted. Suggestions for improving existing translations
can be added without account.
### Github Pull Request
Translators can add their `MY_LOCALE.po` files to the `po` directory
based on `po/labwc.pot` and issue a pull request. To do this they can
generate their `MY_LOCALE.po` file in a few steps:
1. Edit the `po/LINGUAS` file to add their locale name by adding a space
to the end of the field and typing the locale code.
2. Copy the po/labwc.pot to po/MY_LOCALE.po
3. Edit the newly generated MY_LOCALE.po file with some of their
1. Edit the `po/LINGUAS` file to add their locale code in English
alphabetical order to the field of locale codes.
2. Copy the `po/labwc.pot` to `po/MY_LOCALE.po`
3. Edit the newly generated `MY_LOCALE.po` file with some of their
contact and locale details in the header of the file then add the
translation strings under each English string.
[See this tutorial for further guidance](https://www.labri.fr/perso/fleury/posts/programming/a-quick-gettext-tutorial.html)
## Coders
Code contributors may need to update relevant files if their additions
affect UI elements (at the moment only `src/menu/menu.c`). In this case
the `po/labwc.pot` file needs to be updated so that translators can

40
NEWS.md
View file

@ -9,7 +9,7 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------|
| 2024-01-23 | [unreleased] | 0.17.1 | |
| 2024-03-01 | [0.7.1] | 0.17.1 | 18624 |
| 2023-12-22 | [0.7.0] | 0.17.1 | 16576 |
| 2023-11-25 | [0.6.6] | 0.16.2 | 15796 |
| 2023-09-23 | [0.6.5] | 0.16.2 | 14809 |
@ -28,10 +28,33 @@ The format is based on [Keep a Changelog]
| 2021-03-05 | [0.1.0] | 0.12.0 | 4627 |
## [unreleased]
## [0.7.1]
### Added
- Support libinput option sendEventsMode to allow enabling/disabling devices.
Co-Authored-By: @Sachin-Bhat
```xml
<libinput>
<device>
<sendEventsMode>yes|no|disabledOnExternalMouse</sendEventsMode>
</device>
</libinput>
```
- Add click method libinput option. Written-by: @datMaffin
```xml
<libinput>
<device>
<clickMethod>none|buttonAreas|clickfinger</clickMethod>
</device>
</libinput>
```
- Add `data/labwc.svg` & `data/labwc-symbolic.svg`, and specify icon name
in labwc.desktop to enable Display Managers to show an icons for labwc.
- Expose output configuration test to clients. For example, this enables
`wlr-randr --dryrun`
- Add window-edge resistance for interactive moves/resizes and support negative
@ -90,6 +113,7 @@ The format is based on [Keep a Changelog]
- Add config option `<placement><policy>` with supported values `center`,
`cursor` and `automatic`. The latter minimizes overlap with other windows
already on screen and is similar to Openbox's smart window placement.
The placement policies honour `<core><gap>`.
Written-by: @ahesford #1312
```xml
@ -100,6 +124,13 @@ The format is based on [Keep a Changelog]
### Fixed
- Delay popup-unconstrain until after first commit in response to a changed
wlroots 0.17 interface and to get rid of the error message below. Issue #1372
[types/xdg_shell/wlr_xdg_surface.c:169] A configure is scheduled for an uninitialized xdg_surface
- Notify clients about configuration errors when changing output settings.
Issue #1528.
- Fix output configuration bug causing compositor crash when refresh rate is
zero. Issue #1458
- Fix disappearing cursor bug on view destruction. Issue #1393
@ -124,6 +155,8 @@ The format is based on [Keep a Changelog]
### Changed
- Make `MoveToCursor` honour `<core><gap>`. Issue #1494
- Add `Roll Up/Down` client-menu entry for `ToggleShade`
- When a Wayland-native window is snapped to a screen edges or user-defined
region, labwc will notify the application that it is "tiled", allowing the
application to better adapt its rendering to constrained layouts. Windows
@ -1043,7 +1076,8 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
[unreleased]: https://github.com/labwc/labwc/compare/0.7.0...HEAD
[unreleased]: https://github.com/labwc/labwc/compare/0.7.1...HEAD
[0.7.1]: https://github.com/labwc/labwc/compare/0.7.0...0.7.1
[0.7.0]: https://github.com/labwc/labwc/compare/0.6.6...0.7.0
[0.6.6]: https://github.com/labwc/labwc/compare/0.6.5...0.6.6
[0.6.5]: https://github.com/labwc/labwc/compare/0.6.4...0.6.5

View file

@ -127,8 +127,11 @@ High-level summary of items that Labwc supports:
The obligatory screenshot:
<a href="https://i.imgur.com/vOelinT.png">
<img src="https://i.imgur.com/vOelinTl.png">
<a href="https://labwc.github.io/img/scrot1.png">
<img src="https://labwc.github.io/img/scrot1-small.png">
</a><br />
<a href="https://labwc.github.io/obligatory-screenshot.html">
<small>Screenshot description</small>
</a>
## 2. Build and Installation

View file

@ -163,7 +163,7 @@ Actions are used in menus and keyboard/mouse bindings.
to the center of the window. If the given output does not contain
any windows, the cursor is centered on the given output.
*<action name="MoveToOutput" name="HDMI-A-1" direction="value" />*
*<action name="MoveToOutput" name="HDMI-A-1" direction="value" wrap="no" />*
Moves active window to other output, unless the window state is
fullscreen.
@ -172,6 +172,9 @@ Actions are used in menus and keyboard/mouse bindings.
be one of "left", "right", "up" or "down" to indicate that the window
should be moved to the next output in that direction (if one exists).
*wrap* [yes|no] When using the direction attribute, wrap around from
right-to-left or top-to-bottom, and vice versa. Default no.
*<action name="FitToOutput" />*
Resizes active window size to width and height of the output when the
window size exceeds the output size.

View file

@ -8,7 +8,7 @@ labwc - configuration files
Labwc uses openbox-3.6 specification for configuration and theming, but does not
support all options. The following files form the basis of the labwc
configuration: rc.xml, menu.xml, autostart and environment.
configuration: rc.xml, menu.xml, autostart, shutdown and environment.
No configuration files are needed to start and run labwc.
@ -34,11 +34,8 @@ alternative.
The configuration directory location can be override with the -C command line
option.
All configuration and theme files except autostart are re-loaded on receiving
signal SIGHUP.
The *autostart* file is executed as a shell script. This is the place for
executing clients for handling background images, panels and similar.
All configuration and theme files except autostart and shutdown are re-loaded on
receiving signal SIGHUP.
The *environment* file is parsed as *variable=value* and sets environment
variables accordingly. It is recommended to specify keyboard layout settings and
@ -48,6 +45,20 @@ sourced prior to running openbox.
Note: Tilde (~) and environment variables in the value are expanded, but
subshell syntax and apostrophes are ignored.
The *autostart* file is executed as a shell script after labwc has read its
configuration and set variables defined in the environment file. Additionally,
the environment variables WAYLAND_DISPLAY and (when labwc is built with Xwayland
support) DISPLAY will be defined. This is the place for executing clients for
handling background images, panels and other tasks that should run automatically
when labwc launches.
The *shutdown* file is executed as a shell script when labwc is preparing to
terminate itself. All environment variables, including WAYLAND_DISPLAY and
DISPLAY, will be available to the script. However, because the script runs
asynchronously with other termination tasks, the shutdown file should not assume
that the display will be usable. This file is useful to perform any custom
operations necessary to finalize a labwc session.
The *menu.xml* file defines the context/root-menus and is described in
labwc-menu(5).
@ -511,7 +522,7 @@ extending outward from the snapped edge.
```
*<touch deviceName="" />*
A touch configuration can be bound to a specifc device. If device
A touch configuration can be bound to a specific device. If device
name is left empty, the touch configuration applies to all touch
devices or functions as a fallback. Multiple touch configurations
can exist.
@ -592,6 +603,8 @@ extending outward from the snapped edge.
<dragLock></dragLock>
<middleEmulation></middleEmulation>
<disableWhileTyping></disableWhileTyping>
<clickMethod></clickMethod>
<sendEventsMode></sendEventsMode>
</device>
</libinput>
```
@ -667,6 +680,36 @@ extending outward from the snapped edge.
any motion events while a keyboard is typing, and for a short while
after as well.
*<libinput><device><clickMethod>* [none|buttonAreas|clickfinger]
Configure the method by which physical clicks on a touchpad are mapped to
mouse-button events.
The click methods available are:
- *buttonAreas* - The bottom of the touchpad is divided into distinct
regions corresponding to left, middle and right buttons; clicking within
the region will trigger the corresponding event. Clicking the main area
further up produces a left button event.
- *clickfinger* - Clicking with one, two or three finger(s) will produce
left, right or middle button event without regard to the location of a
click.
- *none* - Physical clicks will not produce button events.
The default method depends on the touchpad hardware.
*<libinput><device><sendEventsMode>* [yes|no|disabledOnExternalMouse]
Optionally enable or disable sending any device events.
The options available are:
- *yes* - Events are sent as usual
- *no* - No events are sent from this device
- *disabledOnExternalMouse* - This device does not send events if an
external mouse has been detected.
It is possible to prevent events from a device in the config and then do
a Reconfigure to temporarily enable / disable specific devices.
By default, this setting is not configured.
## WINDOW RULES
Two types of window rules are supported, actions and properties. They are

View file

@ -2,7 +2,7 @@ labwc(1)
# NAME
labwc - a wayland stacking compositor
labwc - a Wayland stacking compositor
# SYNOPSIS
@ -16,8 +16,10 @@ It is light-weight and independent with a focus on simply stacking windows
well and rendering some window decorations. Where practicable it uses clients
for wall-paper, panels, screenshots and so on.
# SIGNALS
The compositor will exit or reload its configuration upon receiving SIGTERM
and SIGHUP respectively. For example:
and SIGHUP respectively. For example:
```
kill -s <signal> $LABWC_PID
@ -40,7 +42,7 @@ the `--exit` and `--reconfigure` options use.
Enable full logging, including debug information
*-e, --exit*
Exit the compositor
Exit the compositor by sending SIGTERM to `$LABWC_PID`
*-h, --help*
Show help message and quit
@ -49,7 +51,7 @@ the `--exit` and `--reconfigure` options use.
Merge user config/theme files in all XDG Base Directories
*-r, --reconfigure*
Reload the compositor configuration
Reload the compositor configuration by sending SIGHUP to `$LABWC_PID`
*-s, --startup* <command>
Run command on startup
@ -60,6 +62,48 @@ the `--exit` and `--reconfigure` options use.
*-V, --verbose*
Enable more verbose logging
# SESSION MANAGEMENT
To enable the use of graphical clients launched via D-Bus or systemd servie
activation, labwc can update both activation environments on launch. Provided
that labwc is aware of an active D-Bus user session (*i.e.*, the environment
variable `DBUS_SESSION_BUS_ADDRESS` is defined), the compositor will invoke the
commands
```
dbus-update-activation-environment
systemctl --user import-environment
```
(when available) to notify D-Bus and systemd with the values of the following
environment variables:
```
WAYLAND_DISPLAY
DISPLAY
XDG_CURRENT_DESKTOP
XDG_SESSION_TYPE
XCURSOR_SIZE
XCURSOR_THEME
LABWC_PWD
```
This behavior is enabled by default whenever labwc uses the "DRM" wlroots
backend (which implies that labwc is the primary compositor on the console).
When other backends are employed (for example, when labwc runs nested in another
Wayland compositor or an X11 server), updates to the activation environment are
disabled by default. Updates to the activation environment can be forced by
setting the environment variable `LABWC_UPDATE_ACTIVATION_ENV` to one of the
truthy values `1`, `true`, `yes` or `on`; or suppressed by setting the variable
to one of the falsy values `0`, `false`, `no` or `off`.
Whenever labwc updates the activation environment on launch, it will also
attempt to clear the activation environment on exit. For D-Bus, which does not
provide a means for properly un-setting variables in the activation environment,
this is accomplished by setting the session variables to empty strings. For
systemd, the command `systemctl --user unset-environment` will be invoked to
actually remove the variables from the activation environment.
# SEE ALSO
labwc-config(5), labwc-theme(5), labwc-actions(5)
labwc-actions(5), labwc-config(5), labwc-menu(5), labwc-theme(5)

View file

@ -7,10 +7,6 @@
<labwc_config>
<core>
<gap>10</gap>
</core>
<theme>
<name></name>
<cornerRadius>8</cornerRadius>

View file

@ -465,13 +465,15 @@
non-touch, default or the name of a device. You can obtain device names by
running *libinput list-devices* as root or member of the input group.
Tap is set to *yes* be default. All others are left blank in order to use
Tap is set to *yes* by default. All others are left blank in order to use
device defaults.
All values are [yes|no] except for:
- pointerSpeed [-1.0 to 1.0]
- accelProfile [flat|adaptive]
- tapButtonMap [lrm|lmr]
- clickMethod [none|buttonAreas|clickfinger]
- sendEventsMode [yes|no|disabledOnExternalMouse]
-->
<libinput>
<device category="default">
@ -485,6 +487,8 @@
<dragLock></dragLock>
<middleEmulation></middleEmulation>
<disableWhileTyping></disableWhileTyping>
<clickMethod></clickMethod>
<sendEventsMode></sendEventsMode>
</device>
</libinput>

View file

@ -43,4 +43,23 @@ void string_truncate_at_pattern(char *buf, const char *pattern);
*/
char *strdup_printf(const char *fmt, ...);
/**
* str_join - format and join an array of strings with a separator
* @parts: NULL-terminated array of string parts to be joined
* @fmt: printf-style format string applied to each part
* @sep: separator inserted between parts when joining
*
* A new string is allocated to hold the joined result. The user must free the
* returned string. Returns NULL on error.
*
* Each part of the array is converted via the equivalent of sprintf(output,
* fmt, part), so fmt should include a single "%s" format specification. If fmt
* is NULL, a default "%s" will be used to copy each part verbatim.
*
* The separator is arbitrary. When the separator is NULL, a single space will
* be used.
*/
char *str_join(const char * const parts[],
const char *restrict fmt, const char *restrict sep);
#endif /* LABWC_STRING_HELPERS_H */

View file

@ -23,11 +23,13 @@ struct libinput_category {
int left_handed;
enum libinput_config_tap_state tap;
enum libinput_config_tap_button_map tap_button_map;
int tap_and_drag; /* -1 or libinput_config_drag_state */
int drag_lock; /* -1 or libinput_config_drag_lock_state */
int accel_profile; /* -1 or libinput_config_accel_profile */
int middle_emu; /* -1 or libinput_config_middle_emulation_state */
int dwt; /* -1 or libinput_config_dwt_state */
int tap_and_drag; /* -1 or libinput_config_drag_state */
int drag_lock; /* -1 or libinput_config_drag_lock_state */
int accel_profile; /* -1 or libinput_config_accel_profile */
int middle_emu; /* -1 or libinput_config_middle_emulation_state */
int dwt; /* -1 or libinput_config_dwt_state */
int click_method; /* -1 or libinput_config_click_method */
int send_events_mode; /* -1 or libinput_config_send_events_mode */
};
enum lab_libinput_device_type get_device_type(const char *s);

View file

@ -2,6 +2,8 @@
#ifndef LABWC_SESSION_H
#define LABWC_SESSION_H
struct server;
/**
* session_environment_init - set enrivonment variables based on <key>=<value>
* pairs in `${XDG_CONFIG_DIRS:-/etc/xdg}/lawbc/environment` with user override
@ -13,6 +15,12 @@ void session_environment_init(void);
* session_autostart_init - run autostart file as shell script
* Note: Same as `sh ~/.config/labwc/autostart` (or equivalent XDG config dir)
*/
void session_autostart_init(void);
void session_autostart_init(struct server *server);
/**
* session_shutdown - run session shutdown file as shell script
* Note: Same as `sh ~/.config/labwc/shutdown` (or equivalent XDG config dir)
*/
void session_shutdown(struct server *server);
#endif /* LABWC_SESSION_H */

View file

@ -3,11 +3,15 @@
#define LABWC_EDGES_H
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include "common/macros.h"
struct border;
struct output;
struct server;
struct view;
struct wlr_box;
static inline int
clipped_add(int a, int b)
@ -102,7 +106,7 @@ void edges_adjust_geom(struct view *view, struct border edges,
void edges_find_neighbors(struct border *nearest_edges, struct view *view,
struct wlr_box target, struct output *output,
edge_validator_t validator, bool use_pending);
edge_validator_t validator, bool use_pending, bool ignore_hidden);
void edges_find_outputs(struct border *nearest_edges, struct view *view,
struct wlr_box target, struct output *output,
@ -116,4 +120,5 @@ void edges_adjust_resize_geom(struct view *view, struct border edges,
bool edges_traverse_edge(struct edge current, struct edge target, struct edge edge);
void edges_calculate_visibility(struct server *server, struct view *ignored_view);
#endif /* LABWC_EDGES_H */

View file

@ -28,6 +28,7 @@ struct lab_layer_popup {
/* To simplify moving popup nodes from the bottom to the top layer */
struct wlr_box output_toplevel_sx_box;
struct wl_listener commit;
struct wl_listener destroy;
struct wl_listener new_popup;
};

View file

@ -166,6 +166,7 @@ struct view {
bool tearing_hint;
bool visible_on_all_workspaces;
enum view_edge tiled;
uint32_t edges_visible; /* enum wlr_edges bitset */
bool inhibits_keybinds;
xkb_layout_index_t keyboard_layout;
@ -502,7 +503,8 @@ void view_on_output_destroy(struct view *view);
void view_connect_map(struct view *view, struct wlr_surface *surface);
void view_destroy(struct view *view);
struct output *view_get_adjacent_output(struct view *view, enum view_edge edge);
struct output *view_get_adjacent_output(struct view *view, enum view_edge edge,
bool wrap);
enum view_axis view_axis_parse(const char *direction);
enum view_edge view_edge_parse(const char *direction);

View file

@ -1,7 +1,7 @@
project(
'labwc',
'c',
version: '0.7.0',
version: '0.7.1',
license: 'GPL-2.0-only',
meson_version: '>=0.59.0',
default_options: [

View file

@ -1 +1 @@
de es et eu fi gl id it ja ka lt nl pa pl pt ru sv tr uk zh_CN
cs de es et eu fi gl hu id it ja ka lt nl pa pl pt ru sv tr uk zh_CN

69
po/cs.po Normal file
View file

@ -0,0 +1,69 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# zenobit <zenobit@disroot.org>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
"PO-Revision-Date: 2024-03-02 02:00+0100\n"
"Last-Translator: zenobit <zenobit@disroot.org>\n"
"Language-Team: Czech <zenobit@disroot.org>\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/menu/menu.c:697
msgid "Reconfigure"
msgstr "Překonfigurovat"
#: src/menu/menu.c:699
msgid "Exit"
msgstr "Odejít"
#: src/menu/menu.c:715
msgid "Minimize"
msgstr "Minimalizovat"
#: src/menu/menu.c:717
msgid "Maximize"
msgstr "Maximalizovat"
#: src/menu/menu.c:719
msgid "Fullscreen"
msgstr "Na celou obrazovku"
#: src/menu/menu.c:721
msgid "Roll up/down"
msgstr "Rolovat nahoru/dolů"
#: src/menu/menu.c:723
msgid "Decorations"
msgstr "Dekorace"
#: src/menu/menu.c:725
msgid "Always on Top"
msgstr "Vždy nahoře"
#: src/menu/menu.c:730
msgid "Move left"
msgstr "Posunout doleva"
#: src/menu/menu.c:737
msgid "Move right"
msgstr "Posunout doprava"
#: src/menu/menu.c:742
msgid "Always on Visible Workspace"
msgstr "Vždy na viditelné Pracovní Ploše"
#: src/menu/menu.c:745
msgid "Workspace"
msgstr "Pracovní Plocha"
#: src/menu/menu.c:748
msgid "Close"
msgstr "Zavřít"

View file

@ -8,13 +8,16 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"PO-Revision-Date: 2024-02-29 14:23+0000\n"
"Last-Translator: Jouni Järvinen <jounijarvis@gmail.com>\n"
"Language-Team: Finnish <https://translate.lxqt-project.org/projects/labwc/"
"labwc/fi/>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:697
msgid "Reconfigure"
@ -22,7 +25,7 @@ msgstr ""
#: src/menu/menu.c:699
msgid "Exit"
msgstr ""
msgstr "Poistu"
#: src/menu/menu.c:715
msgid "Minimize"

72
po/hu.po Normal file
View file

@ -0,0 +1,72 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
"PO-Revision-Date: 2024-02-19 21:23+0000\n"
"Last-Translator: winerysearch <david.fitala@gmail.com>\n"
"Language-Team: Hungarian <https://translate.lxqt-project.org/projects/labwc/"
"labwc/hu/>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:697
msgid "Reconfigure"
msgstr "Rekonfigurál"
#: src/menu/menu.c:699
msgid "Exit"
msgstr "Kilépés"
#: src/menu/menu.c:715
msgid "Minimize"
msgstr "Kis méret"
#: src/menu/menu.c:717
msgid "Maximize"
msgstr "Teljes méret"
#: src/menu/menu.c:719
msgid "Fullscreen"
msgstr "Teljes képernyő"
#: src/menu/menu.c:721
msgid "Roll up/down"
msgstr "Felhúz / Legördül"
#: src/menu/menu.c:723
msgid "Decorations"
msgstr "Dekorációk"
#: src/menu/menu.c:725
msgid "Always on Top"
msgstr "Mindig felül"
#: src/menu/menu.c:730
msgid "Move left"
msgstr "Balra dokkol"
#: src/menu/menu.c:737
msgid "Move right"
msgstr "Jobbra dokkol"
#: src/menu/menu.c:742
msgid "Always on Visible Workspace"
msgstr "Kitűz"
#: src/menu/menu.c:745
msgid "Workspace"
msgstr "Munkaasztal"
#: src/menu/menu.c:748
msgid "Close"
msgstr "Bezárás"

View file

@ -8,14 +8,17 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
"PO-Revision-Date: 2024-01-04 15:23+0000\n"
"PO-Revision-Date: 2024-02-29 14:23+0000\n"
"Last-Translator: Moo <hazap@hotmail.com>\n"
"Language-Team: Lithuanian <https://translate.lxqt-project.org/projects/labwc/labwc/lt/>\n"
"Language-Team: Lithuanian <https://translate.lxqt-project.org/projects/labwc/"
"labwc/lt/>\n"
"Language: lt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2);\n"
"Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > "
"19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? "
"1 : 2);\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:697
@ -40,7 +43,7 @@ msgstr "Visas ekranas"
#: src/menu/menu.c:721
msgid "Roll up/down"
msgstr ""
msgstr "Užraityti/atraityti"
#: src/menu/menu.c:723
msgid "Decorations"

View file

@ -8,9 +8,10 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
"PO-Revision-Date: 2023-12-26 07:23+0000\n"
"PO-Revision-Date: 2024-02-21 14:23+0000\n"
"Last-Translator: A S Alam <amanpreet.alam@gmail.com>\n"
"Language-Team: Punjabi <https://translate.lxqt-project.org/projects/labwc/labwc/pa/>\n"
"Language-Team: Punjabi <https://translate.lxqt-project.org/projects/labwc/"
"labwc/pa/>\n"
"Language: pa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -40,7 +41,7 @@ msgstr "ਪੂਰੀ ਸਕਰੀਨ"
#: src/menu/menu.c:721
msgid "Roll up/down"
msgstr ""
msgstr "ਉੱਤੇ/ਹੇਠਾਂ ਸਕਰਾਓ"
#: src/menu/menu.c:723
msgid "Decorations"

View file

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
"PO-Revision-Date: 2024-01-30 08:23+0000\n"
"Last-Translator: pixis1 <vaninpixel89@gmail.com>\n"
"PO-Revision-Date: 2024-02-26 12:23+0000\n"
"Last-Translator: Alice Ventus <zolkin.ag@phystech.edu>\n"
"Language-Team: Russian <https://translate.lxqt-project.org/projects/labwc/"
"labwc/ru/>\n"
"Language: ru\n"
@ -41,7 +41,6 @@ msgid "Fullscreen"
msgstr "На весь экран"
#: src/menu/menu.c:721
#, fuzzy
msgid "Roll up/down"
msgstr "Свернуть/развернуть в заголовок"

View file

@ -8,9 +8,10 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
"PO-Revision-Date: 2023-12-26 09:00+0000\n"
"PO-Revision-Date: 2024-02-24 22:23+0000\n"
"Last-Translator: Sabri Ünal <libreajans@gmail.com>\n"
"Language-Team: Turkish <https://translate.lxqt-project.org/projects/labwc/labwc/tr/>\n"
"Language-Team: Turkish <https://translate.lxqt-project.org/projects/labwc/"
"labwc/tr/>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -40,7 +41,7 @@ msgstr "Tam Ekran"
#: src/menu/menu.c:721
msgid "Roll up/down"
msgstr ""
msgstr "Yukarı/aşağı katla"
#: src/menu/menu.c:723
msgid "Decorations"

View file

@ -18,6 +18,7 @@ server_protocols = [
wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',

View file

@ -397,6 +397,10 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
}
goto cleanup;
}
if (!strcmp(argument, "wrap")) {
action_arg_add_bool(action, argument, parse_bool(content, false));
goto cleanup;
}
break;
case ACTION_TYPE_VIRTUAL_OUTPUT_ADD:
case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE:
@ -924,10 +928,15 @@ actions_run(struct view *activator, struct server *server,
} else {
/* Config parsing makes sure that direction is a valid direction */
enum view_edge edge = action_get_int(action, "direction", 0);
target = view_get_adjacent_output(view, edge);
bool wrap = action_get_bool(action, "wrap", false);
target = view_get_adjacent_output(view, edge, wrap);
}
if (!target) {
wlr_log(WLR_ERROR, "Invalid output.");
/*
* Most likely because we're already on the
* output furthest in the requested direction.
*/
wlr_log(WLR_DEBUG, "Invalid output");
break;
}
view_move_to_output(view, target);

View file

@ -14,12 +14,16 @@ parse_bool(const char *str, int default_value)
return true;
} else if (!strcasecmp(str, "on")) {
return true;
} else if (!strcmp(str, "1")) {
return true;
} else if (!strcasecmp(str, "no")) {
return false;
} else if (!strcasecmp(str, "false")) {
return false;
} else if (!strcasecmp(str, "off")) {
return false;
} else if (!strcmp(str, "0")) {
return false;
}
error_not_a_boolean:
wlr_log(WLR_ERROR, "(%s) is not a boolean value", str);

View file

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
@ -83,3 +84,73 @@ strdup_printf(const char *fmt, ...)
}
return p;
}
char *
str_join(const char * const parts[],
const char *restrict fmt, const char *restrict sep)
{
assert(parts);
if (!fmt) {
fmt = "%s";
}
if (!sep) {
sep = " ";
}
size_t size = 0;
size_t n_parts = 0;
size_t sep_len = strlen(sep);
/* Count the length of each formatted string */
for (const char *const *s = parts; *s; ++s) {
int n = snprintf(NULL, 0, fmt, *s);
if (n < 0) {
return NULL;
}
size += (size_t)n;
++n_parts;
}
if (n_parts < 1) {
return NULL;
}
/* Need (n_parts - 1) separators, plus one NULL terminator */
size += (n_parts - 1) * sep_len + 1;
/* Concatenate the strings and separators */
char *buf = xzalloc(size);
char *p = buf;
for (const char *const *s = parts; *s; ++s) {
int n = 0;
if (p != buf) {
n = snprintf(p, size, "%s", sep);
if (n < 0 || (size_t)n >= size) {
p = NULL;
break;
}
size -= (size_t)n;
p += (size_t)n;
}
n = snprintf(p, size, fmt, *s);
if (n < 0 || (size_t)n >= size) {
p = NULL;
break;
}
size -= (size_t)n;
p += (size_t)n;
}
if (!p) {
free(buf);
return NULL;
}
return buf;
}

View file

@ -23,6 +23,8 @@ libinput_category_init(struct libinput_category *l)
l->accel_profile = -1;
l->middle_emu = -1;
l->dwt = -1;
l->click_method = -1;
l->send_events_mode = -1;
}
enum lab_libinput_device_type

View file

@ -461,6 +461,29 @@ get_accel_profile(const char *s)
return -1;
}
static int
get_send_events_mode(const char *s)
{
if (!s) {
goto err;
}
int ret = parse_bool(s, -1);
if (ret >= 0) {
return ret
? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
: LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
}
if (!strcasecmp(s, "disabledOnExternalMouse")) {
return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
}
err:
wlr_log(WLR_INFO, "Not a recognised send events mode");
return -1;
}
static void
fill_libinput_category(char *nodename, char *content)
{
@ -563,6 +586,22 @@ fill_libinput_category(char *nodename, char *content)
current_libinput_category->dwt = ret
? LIBINPUT_CONFIG_DWT_ENABLED
: LIBINPUT_CONFIG_DWT_DISABLED;
} else if (!strcasecmp(nodename, "clickMethod")) {
if (!strcasecmp(content, "none")) {
current_libinput_category->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_NONE;
} else if (!strcasecmp(content, "clickfinger")) {
current_libinput_category->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
} else if (!strcasecmp(content, "buttonAreas")) {
current_libinput_category->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
} else {
wlr_log(WLR_ERROR, "invalid clickMethod");
}
} else if (!strcasecmp(nodename, "sendEventsMode")) {
current_libinput_category->send_events_mode =
get_send_events_mode(content);
}
}

View file

@ -1,19 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <wlr/backend/drm.h>
#include <wlr/backend/multi.h>
#include <wlr/util/log.h>
#include "common/buf.h"
#include "common/dir.h"
#include "common/file-helpers.h"
#include "common/mem.h"
#include "common/parse-bool.h"
#include "common/spawn.h"
#include "common/string-helpers.h"
#include "config/session.h"
#include "labwc.h"
static const char *const env_vars[] = {
"DISPLAY",
"WAYLAND_DISPLAY",
"XDG_CURRENT_DESKTOP",
"XCURSOR_SIZE",
"XCURSOR_THEME",
"XDG_SESSION_TYPE",
"LABWC_PID",
NULL
};
static void
process_line(char *line)
{
@ -65,23 +81,71 @@ read_environment_file(const char *filename)
}
static void
update_activation_env(const char *env_keys)
backend_check_drm(struct wlr_backend *backend, void *is_drm)
{
if (wlr_backend_is_drm(backend)) {
*(bool *)is_drm = true;
}
}
static bool
should_update_activation(struct server *server)
{
assert(server);
static const char *act_env = "LABWC_UPDATE_ACTIVATION_ENV";
char *env = getenv(act_env);
if (env) {
/* Respect any valid preference from the environment */
int enabled = parse_bool(env, -1);
if (enabled == -1) {
wlr_log(WLR_ERROR, "ignoring non-Boolean variable %s", act_env);
} else {
wlr_log(WLR_DEBUG, "%s is %s",
act_env, enabled ? "true" : "false");
return enabled;
}
}
/* With no valid preference, update when a DRM backend is in use */
bool have_drm = false;
wlr_multi_for_each_backend(server->backend, backend_check_drm, &have_drm);
return have_drm;
}
static void
update_activation_env(struct server *server, bool initialize)
{
if (!should_update_activation(server)) {
return;
}
if (!getenv("DBUS_SESSION_BUS_ADDRESS")) {
/* Prevent accidentally auto-launching a dbus session */
wlr_log(WLR_INFO, "Not updating dbus execution environment: "
"DBUS_SESSION_BUS_ADDRESS not set");
return;
}
wlr_log(WLR_INFO, "Updating dbus execution environment");
char *cmd = strdup_printf("dbus-update-activation-environment %s", env_keys);
char *env_keys = str_join(env_vars, "%s", " ");
char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " ");
char *cmd =
strdup_printf("dbus-update-activation-environment %s",
initialize ? env_keys : env_unset_keys);
spawn_async_no_shell(cmd);
free(cmd);
cmd = strdup_printf("systemctl --user import-environment %s", env_keys);
cmd = strdup_printf("systemctl --user %s %s",
initialize ? "import-environment" : "unset-environment", env_keys);
spawn_async_no_shell(cmd);
free(cmd);
free(env_keys);
free(env_unset_keys);
}
void
@ -120,14 +184,11 @@ session_environment_init(void)
paths_destroy(&paths);
}
void
session_autostart_init(void)
static void
run_session_script(const char *script)
{
/* Update dbus and systemd user environment, each may fail gracefully */
update_activation_env("DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP");
struct wl_list paths;
paths_config_create(&paths, "autostart");
paths_config_create(&paths, script);
bool should_merge_config = rc.merge_config;
struct wl_list *(*iter)(struct wl_list *list);
@ -138,7 +199,7 @@ session_autostart_init(void)
if (!file_exists(path->string)) {
continue;
}
wlr_log(WLR_INFO, "run autostart file %s", path->string);
wlr_log(WLR_INFO, "run session script %s", path->string);
char *cmd = strdup_printf("sh %s", path->string);
spawn_async_no_shell(cmd);
free(cmd);
@ -149,3 +210,20 @@ session_autostart_init(void)
}
paths_destroy(&paths);
}
void
session_autostart_init(struct server *server)
{
/* Update dbus and systemd user environment, each may fail gracefully */
update_activation_env(server, /* initialize */ true);
run_session_script("autostart");
}
void
session_shutdown(struct server *server)
{
run_session_script("shutdown");
/* Clear the dbus and systemd user environment, each may fail gracefully */
update_activation_env(server, /* initialize */ false);
}

View file

@ -1,19 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_scene.h>
#include "common/graphic-helpers.h"
#include "common/scene-helpers.h"
#include "debug.h"
#include "labwc.h"
#include "node.h"
#include "ssd.h"
#include "view.h"
#include "workspaces.h"
#define HEADER_CHARS "------------------------------"
#define INDENT_SIZE 3
#define LEFT_COL_SPACE 35
#define IGNORE_SSD true
#define IGNORE_MENU true
#define LEFT_COL_SPACE 35
#define IGNORE_OSD_PREVIEW_OUTLINE true
static struct view *last_view;
@ -42,13 +46,13 @@ get_layer_name(uint32_t layer)
{
switch (layer) {
case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
return "layer-background";
return "output->layer-background";
case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
return "layer-bottom";
return "output->layer-bottom";
case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
return "layer-top";
return "output->layer-top";
case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
return "layer-overlay";
return "output->layer-overlay";
default:
abort();
}
@ -57,16 +61,27 @@ get_layer_name(uint32_t layer)
static const char *
get_view_part(struct view *view, struct wlr_scene_node *node)
{
if (view && node == &view->scene_tree->node) {
return "view";
static char view_name[LEFT_COL_SPACE];
if (!view) {
return NULL;
}
if (view && node == view->scene_node) {
if (node == &view->scene_tree->node) {
const char *app_id = view_get_string_prop(view, "app_id");
if (!app_id) {
return "view";
}
snprintf(view_name, sizeof(view_name), "view (%s)", app_id);
return view_name;
}
if (node == view->scene_node) {
return "view->scene_node";
}
if (view) {
return ssd_debug_get_node_name(view->ssd, node);
if (view->resize_indicator.tree
&& node == &view->resize_indicator.tree->node) {
/* Created on-demand */
return "view->resize_indicator";
}
return NULL;
return ssd_debug_get_node_name(view->ssd, node);
}
static const char *
@ -81,12 +96,20 @@ get_special(struct server *server, struct wlr_scene_node *node)
if (node == &server->view_tree->node) {
return "server->view_tree";
}
if (node == &server->view_tree_always_on_bottom->node) {
return "server->always_on_bottom";
}
if (node == &server->view_tree_always_on_top->node) {
return "server->view_tree_always_on_top";
return "server->always_on_top";
}
if (node->parent == server->view_tree) {
/* Add node_descriptor just to get the name here? */
return "workspace";
struct workspace *workspace;
wl_list_for_each(workspace, &server->workspaces, link) {
if (&workspace->tree->node == node) {
return workspace->name;
}
}
return "unknown workspace";
}
if (node->parent == &server->scene->tree) {
struct output *output;
@ -95,15 +118,34 @@ get_special(struct server *server, struct wlr_scene_node *node)
return "output->osd_tree";
}
if (node == &output->layer_popup_tree->node) {
return "output->popup_tree";
return "output->layer_popup_tree";
}
for (int i = 0; i < 4; i++) {
if (node == &output->layer_tree[i]->node) {
return get_layer_name(i);
}
}
if (node == &output->session_lock_tree->node) {
return "output->session_lock_tree";
}
}
}
if (node == &server->xdg_popup_tree->node) {
return "server->xdg_popup_tree";
}
if (node == &server->seat.drag.icons->node) {
return "seat->drag.icons";
}
if (server->seat.region_overlay.tree
&& node == &server->seat.region_overlay.tree->node) {
/* Created on-demand */
return "seat->region_overlay";
}
if (server->osd_state.preview_outline
&& node == &server->osd_state.preview_outline->tree->node) {
/* Created on-demand */
return "osd_state->preview_outline";
}
#if HAVE_XWAYLAND
if (node == &server->unmanaged_tree->node) {
return "server->unmanaged_tree";
@ -155,15 +197,21 @@ dump_tree(struct server *server, struct wlr_scene_node *node,
HEADER_CHARS, HEADER_CHARS, HEADER_CHARS);
printf(" ");
}
int padding = LEFT_COL_SPACE - pos - strlen(type);
int max_width = LEFT_COL_SPACE - pos;
int padding = max_width - strlen(type);
if (padding < 0) {
padding = 0;
}
if (!pos) {
padding += 3;
}
printf("%s %*c %4d %4d [%p]\n", type, padding, ' ', x, y, node);
printf("%.*s %*c %4d %4d [%p]\n", max_width - 1, type, padding, ' ', x, y, node);
if ((IGNORE_MENU && node == &server->menu_tree->node)
|| (IGNORE_SSD && last_view
&& ssd_debug_is_root_node(last_view->ssd, node))) {
&& ssd_debug_is_root_node(last_view->ssd, node))
|| (IGNORE_OSD_PREVIEW_OUTLINE && server->osd_state.preview_outline
&& node == &server->osd_state.preview_outline->tree->node)) {
printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "<skipping children>");
return;
}

View file

@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <limits.h>
#include <pixman.h>
#include <wlr/util/edges.h>
#include <wlr/util/box.h>
#include "common/border.h"
#include "common/macros.h"
@ -8,6 +10,7 @@
#include "edges.h"
#include "labwc.h"
#include "view.h"
#include "node.h"
static void
edges_for_target_geometry(struct border *edges, struct view *view,
@ -35,34 +38,54 @@ edges_initialize(struct border *edges)
}
static inline struct edge
build_edge(struct border region, enum view_edge direction, int pad)
build_edge(struct border region, enum wlr_edges direction, int pad)
{
struct edge edge = { 0 };
switch (direction) {
case VIEW_EDGE_LEFT:
case WLR_EDGE_LEFT:
edge.offset = clipped_sub(region.left, pad);
edge.min = region.top;
edge.max = region.bottom;
break;
case VIEW_EDGE_RIGHT:
case WLR_EDGE_RIGHT:
edge.offset = clipped_add(region.right, pad);
edge.min = region.top;
edge.max = region.bottom;
break;
case VIEW_EDGE_UP:
case WLR_EDGE_TOP:
edge.offset = clipped_sub(region.top, pad);
edge.min = region.left;
edge.max = region.right;
break;
case VIEW_EDGE_DOWN:
case WLR_EDGE_BOTTOM:
edge.offset = clipped_add(region.bottom, pad);
edge.min = region.left;
edge.max = region.right;
break;
default:
case WLR_EDGE_NONE:
/* Should never be reached */
assert(false);
wlr_log(WLR_ERROR, "invalid direction");
abort();
}
return edge;
}
static inline bool
is_lesser(enum wlr_edges direction)
{
return direction == WLR_EDGE_LEFT || direction == WLR_EDGE_TOP;
}
static inline struct edge
build_visible_edge(struct border region, enum wlr_edges direction,
int pad, uint32_t edges_visible)
{
struct edge edge = build_edge(region, direction, pad);
if (!(edges_visible & direction)) {
edge.offset = is_lesser(direction) ? INT_MIN : INT_MAX;
}
return edge;
@ -72,7 +95,7 @@ static void
validate_single_region_edge(int *valid_edge,
struct border view, struct border target,
struct border region, edge_validator_t validator,
enum view_edge direction)
enum wlr_edges direction, uint32_t edges_visible)
{
/*
* When a view snaps to another while moving to its target, it can do
@ -90,42 +113,63 @@ validate_single_region_edge(int *valid_edge,
* the region borders for aligned edges only.
*/
bool lesser = direction == VIEW_EDGE_LEFT || direction == VIEW_EDGE_UP;
enum wlr_edges opposing = WLR_EDGE_NONE;
switch (direction) {
case WLR_EDGE_TOP:
opposing = WLR_EDGE_BOTTOM;
break;
case WLR_EDGE_BOTTOM:
opposing = WLR_EDGE_TOP;
break;
case WLR_EDGE_LEFT:
opposing = WLR_EDGE_RIGHT;
break;
case WLR_EDGE_RIGHT:
opposing = WLR_EDGE_LEFT;
break;
case WLR_EDGE_NONE:
/* Should never be reached */
assert(false);
return;
}
validator(valid_edge,
build_edge(view, direction, 0),
build_edge(target, direction, 0),
build_edge(region, view_edge_invert(direction), 0),
build_edge(region, direction, rc.gap), lesser);
build_visible_edge(region, opposing, 0, edges_visible),
build_visible_edge(region, direction, rc.gap, edges_visible),
is_lesser(direction));
}
static void
validate_edges(struct border *valid_edges,
struct border view, struct border target,
struct border region, edge_validator_t validator)
struct border region, uint32_t edges_visible,
edge_validator_t validator)
{
/* Check for edges encountered during movement of left edge */
validate_single_region_edge(&valid_edges->left,
view, target, region, validator, VIEW_EDGE_LEFT);
view, target, region, validator, WLR_EDGE_LEFT, edges_visible);
/* Check for edges encountered during movement of right edge */
validate_single_region_edge(&valid_edges->right,
view, target, region, validator, VIEW_EDGE_RIGHT);
view, target, region, validator, WLR_EDGE_RIGHT, edges_visible);
/* Check for edges encountered during movement of top edge */
validate_single_region_edge(&valid_edges->top,
view, target, region, validator, VIEW_EDGE_UP);
view, target, region, validator, WLR_EDGE_TOP, edges_visible);
/* Check for edges encountered during movement of bottom edge */
validate_single_region_edge(&valid_edges->bottom,
view, target, region, validator, VIEW_EDGE_DOWN);
view, target, region, validator, WLR_EDGE_BOTTOM, edges_visible);
}
static void
validate_single_output_edge(int *valid_edge,
struct border view, struct border target,
struct border region, edge_validator_t validator,
enum view_edge direction)
enum wlr_edges direction)
{
static struct border unbounded = {
.top = INT_MIN,
@ -134,13 +178,11 @@ validate_single_output_edge(int *valid_edge,
.left = INT_MIN,
};
bool lesser = direction == VIEW_EDGE_LEFT || direction == VIEW_EDGE_UP;
validator(valid_edge,
build_edge(view, direction, 0),
build_edge(target, direction, 0),
build_edge(region, direction, 0),
build_edge(unbounded, direction, 0), lesser);
build_edge(unbounded, direction, 0), is_lesser(direction));
}
static void
@ -182,32 +224,170 @@ validate_output_edges(struct border *valid_edges,
/* Left edge encounters a half-infinite region to the left of the output */
validate_single_output_edge(&valid_edges->left,
view, target, output, validator, VIEW_EDGE_LEFT);
view, target, output, validator, WLR_EDGE_LEFT);
/* Right edge encounters a half-infinite region to the right of the output */
validate_single_output_edge(&valid_edges->right,
view, target, output, validator, VIEW_EDGE_RIGHT);
view, target, output, validator, WLR_EDGE_RIGHT);
/* Top edge encounters a half-infinite region above the output */
validate_single_output_edge(&valid_edges->top,
view, target, output, validator, VIEW_EDGE_UP);
view, target, output, validator, WLR_EDGE_TOP);
/* Bottom edge encounters a half-infinite region below the output */
validate_single_output_edge(&valid_edges->bottom,
view, target, output, validator, VIEW_EDGE_DOWN);
view, target, output, validator, WLR_EDGE_BOTTOM);
}
/* Test if parts of the current view is covered by the remaining space in the region */
static void
subtract_view_from_space(struct view *view, pixman_region32_t *available)
{
struct wlr_box view_size = ssd_max_extents(view);
pixman_box32_t view_rect = {
.x1 = view_size.x,
.x2 = view_size.x + view_size.width,
.y1 = view_size.y,
.y2 = view_size.y + view_size.height
};
pixman_region_overlap_t overlap =
pixman_region32_contains_rectangle(available, &view_rect);
switch (overlap) {
case PIXMAN_REGION_IN:
view->edges_visible = WLR_EDGE_TOP | WLR_EDGE_RIGHT
| WLR_EDGE_BOTTOM | WLR_EDGE_LEFT;
break;
case PIXMAN_REGION_OUT:
view->edges_visible = 0;
return;
case PIXMAN_REGION_PART:
; /* works around "a label can only be part of a statement" */
pixman_region32_t intersection;
pixman_region32_init(&intersection);
pixman_region32_intersect_rect(&intersection, available,
view_size.x, view_size.y,
view_size.width, view_size.height);
int nrects;
const pixman_box32_t *rects =
pixman_region32_rectangles(&intersection, &nrects);
view->edges_visible = 0;
for (int i = 0; i < nrects; i++) {
if (rects[i].x1 == view_rect.x1) {
view->edges_visible |= WLR_EDGE_LEFT;
}
if (rects[i].y1 == view_rect.y1) {
view->edges_visible |= WLR_EDGE_TOP;
}
if (rects[i].x2 == view_rect.x2) {
view->edges_visible |= WLR_EDGE_RIGHT;
}
if (rects[i].y2 == view_rect.y2) {
view->edges_visible |= WLR_EDGE_BOTTOM;
}
}
pixman_region32_fini(&intersection);
break;
}
/* Subtract the view geometry from the available region for the next check */
pixman_region32_t view_region;
pixman_region32_init_rects(&view_region, &view_rect, 1);
pixman_region32_subtract(available, available, &view_region);
pixman_region32_fini(&view_region);
}
static void
subtract_node_tree(struct wlr_scene_tree *tree, pixman_region32_t *available,
struct view *ignored_view)
{
struct view *view;
struct wlr_scene_node *node;
struct node_descriptor *node_desc;
wl_list_for_each_reverse(node, &tree->children, link) {
if (!node->enabled) {
/*
* This skips everything that is not being
* rendered, including minimized / unmapped
* windows and workspaces other than the
* current one.
*/
continue;
}
node_desc = node->data;
if (node_desc && node_desc->type == LAB_NODE_DESC_VIEW) {
view = node_view_from_node(node);
if (view != ignored_view) {
subtract_view_from_space(view, available);
}
} else if (node->type == WLR_SCENE_NODE_TREE) {
subtract_node_tree(wlr_scene_tree_from_node(node),
available, ignored_view);
}
}
}
void
edges_calculate_visibility(struct server *server, struct view *ignored_view)
{
/*
* The region stores the available output layout space
* and subtracts the window geometries in reverse rendering
* order, e.g. a window rendered on top is subtracted first.
*
* This allows to detect if a window is actually visible.
* If there is no overlap of its geometry and the remaining
* region it must be completely covered by other windows.
*
*/
pixman_region32_t region;
pixman_region32_init(&region);
/*
* Initialize the region with each individual output.
*
* If we were to use NULL for the reference output we
* would get a single combined wlr_box of the whole
* layout which could cover actual invisible areas
* in case the output resolutions differ.
*/
struct output *output;
struct wlr_box layout_box;
wl_list_for_each(output, &server->outputs, link) {
if (!output_is_usable(output)) {
continue;
}
wlr_output_layout_get_box(server->output_layout,
output->wlr_output, &layout_box);
pixman_region32_union_rect(&region, &region,
layout_box.x, layout_box.y, layout_box.width, layout_box.height);
}
subtract_node_tree(&server->scene->tree, &region, ignored_view);
pixman_region32_fini(&region);
}
void
edges_find_neighbors(struct border *nearest_edges, struct view *view,
struct wlr_box target, struct output *output,
edge_validator_t validator, bool use_pending)
edge_validator_t validator, bool use_pending, bool ignore_hidden)
{
assert(view);
assert(validator);
assert(nearest_edges);
if (!output_is_usable(view->output)) {
wlr_log(WLR_DEBUG, "ignoring edge search for view on unsable output");
return;
}
struct border view_edges = { 0 };
struct border target_edges = { 0 };
@ -223,22 +403,21 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
continue;
}
if (output && v->output != output) {
uint32_t edges_visible = ignore_hidden ? v->edges_visible :
WLR_EDGE_TOP | WLR_EDGE_LEFT
| WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT;
if (edges_visible == 0) {
continue;
}
/*
* If view and v are on different outputs, make sure part of
* view is actually in the usable area of the output of v.
*/
if (view->output != v->output) {
struct wlr_box usable =
output_usable_area_in_layout_coords(v->output);
if (output && output != v->output && !view_on_output(v, output)) {
continue;
}
struct wlr_box ol;
if (!wlr_box_intersection(&ol, view_geom, &usable)) {
continue;
}
/* Both view and v must share a common output */
if (view->output != v->output && !(view->outputs & v->outputs)) {
continue;
}
struct border border = ssd_get_margin(v->ssd);
@ -252,7 +431,7 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
};
validate_edges(nearest_edges, view_edges,
target_edges, win_edges, validator);
target_edges, win_edges, edges_visible, validator);
}
}
@ -265,6 +444,12 @@ edges_find_outputs(struct border *nearest_edges, struct view *view,
assert(validator);
assert(nearest_edges);
if (!output_is_usable(view->output)) {
wlr_log(WLR_DEBUG,
"ignoring edge search for view on unsable output");
return;
}
struct border view_edges = { 0 };
struct border target_edges = { 0 };

View file

@ -79,9 +79,7 @@ key_state_pressed_sent_keycodes(void)
report(&bound, "before - bound:");
/* pressed_sent = pressed - bound */
memcpy(pressed_sent.keys, pressed.keys,
MAX_PRESSED_KEYS * sizeof(uint32_t));
pressed_sent.nr_keys = pressed.nr_keys;
pressed_sent = pressed;
for (int i = 0; i < bound.nr_keys; ++i) {
remove_key(&pressed_sent, bound.keys[i]);
}

View file

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "edges.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "regions.h"
@ -123,6 +124,9 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
if (rc.resize_indicator) {
resize_indicator_show(view);
}
if (rc.window_edge_strength) {
edges_calculate_visibility(server, view);
}
}
/* Returns true if view was snapped to any edge */

View file

@ -260,14 +260,35 @@ popup_handle_destroy(struct wl_listener *listener, void *data)
wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
/* Usually already removed unless there was no commit at all */
if (popup->commit.notify) {
wl_list_remove(&popup->commit.link);
}
free(popup);
}
static void
popup_handle_commit(struct wl_listener *listener, void *data)
{
struct lab_layer_popup *popup =
wl_container_of(listener, popup, commit);
if (popup->wlr_popup->base->initial_commit) {
wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup,
&popup->output_toplevel_sx_box);
/* Prevent getting called over and over again */
wl_list_remove(&popup->commit.link);
popup->commit.notify = NULL;
}
}
static void popup_handle_new_popup(struct wl_listener *listener, void *data);
static struct lab_layer_popup *
create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent,
struct wlr_box *output_toplevel_sx_box)
create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent)
{
struct lab_layer_popup *popup = znew(*popup);
popup->wlr_popup = wlr_popup;
@ -282,10 +303,13 @@ create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent,
popup->destroy.notify = popup_handle_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
wlr_xdg_popup_unconstrain_from_box(wlr_popup, output_toplevel_sx_box);
popup->commit.notify = popup_handle_commit;
wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
return popup;
}
@ -297,8 +321,7 @@ popup_handle_new_popup(struct wl_listener *listener, void *data)
wl_container_of(listener, lab_layer_popup, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
struct lab_layer_popup *new_popup = create_popup(wlr_popup,
lab_layer_popup->scene_tree,
&lab_layer_popup->output_toplevel_sx_box);
lab_layer_popup->scene_tree);
new_popup->output_toplevel_sx_box =
lab_layer_popup->output_toplevel_sx_box;
}
@ -357,8 +380,7 @@ handle_new_popup(struct wl_listener *listener, void *data)
.width = output_box.width,
.height = output_box.height,
};
struct lab_layer_popup *popup = create_popup(wlr_popup,
surface->tree, &output_toplevel_sx_box);
struct lab_layer_popup *popup = create_popup(wlr_popup, surface->tree);
popup->output_toplevel_sx_box = output_toplevel_sx_box;
if (surface->layer_surface->current.layer

View file

@ -171,13 +171,15 @@ main(int argc, char *argv[])
menu_init(&server);
session_autostart_init();
session_autostart_init(&server);
if (startup_cmd) {
spawn_async_no_shell(startup_cmd);
}
wl_display_run(server.wl_display);
session_shutdown(&server);
server_finish(&server);
menu_finish(&server);

View file

@ -150,6 +150,32 @@ output_request_state_notify(struct wl_listener *listener, void *data)
struct output *output = wl_container_of(listener, output, request_state);
const struct wlr_output_event_request_state *event = data;
/*
* If wlroots ever requests other state changes here we could
* restore more of ddc9047a67cd53b2948f71fde1bbe9118000dd3f.
*/
if (event->state->committed == WLR_OUTPUT_STATE_MODE) {
/* Only the mode has changed */
switch (event->state->mode_type) {
case WLR_OUTPUT_STATE_MODE_FIXED:
wlr_output_set_mode(output->wlr_output, event->state->mode);
break;
case WLR_OUTPUT_STATE_MODE_CUSTOM:
wlr_output_set_custom_mode(output->wlr_output,
event->state->custom_mode.width,
event->state->custom_mode.height,
event->state->custom_mode.refresh);
break;
}
wlr_output_schedule_frame(output->wlr_output);
return;
}
/*
* Fallback path for everything that we didn't handle above.
* The commit will cause a black frame injection so this
* path causes flickering during resize of nested outputs.
*/
if (!wlr_output_commit_state(output->wlr_output, event->state)) {
wlr_log(WLR_ERROR, "Backend requested a new state that could not be applied");
}
@ -405,10 +431,11 @@ output_update_for_layout_change(struct server *server)
cursor_update_image(&server->seat);
}
static void
static bool
output_config_apply(struct server *server,
struct wlr_output_configuration_v1 *config)
{
bool success = true;
server->pending_output_layout_change++;
struct wlr_output_configuration_head_v1 *head;
@ -436,8 +463,15 @@ output_config_apply(struct server *server,
output_enable_adaptive_sync(o, head->state.adaptive_sync_enabled);
}
if (!wlr_output_commit(o)) {
wlr_log(WLR_ERROR, "Output config commit failed");
continue;
/*
* FIXME: This is only part of the story, we should revert
* all previously commited outputs as well here.
*
* See https://github.com/labwc/labwc/pull/1528
*/
wlr_log(WLR_INFO, "Output config commit failed: %s", o->name);
success = false;
break;
}
/* Only do Layout specific actions if the commit went trough */
@ -477,6 +511,7 @@ output_config_apply(struct server *server,
server->pending_output_layout_change--;
do_output_layout_change(server);
return success;
}
static bool
@ -509,11 +544,17 @@ verify_output_config_v1(const struct wlr_output_configuration_v1 *config)
if (wlr_output_is_wl(head->state.output) && refresh != 0) {
/* Wayland backend does not support refresh rates */
err_msg = "Wayland backend refresh rate unsupported";
err_msg = "Wayland backend refresh rates unsupported";
goto custom_mode_failed;
}
}
if (wlr_output_is_wl(head->state.output)
&& !head->state.adaptive_sync_enabled) {
err_msg = "Wayland backend requires adaptive sync";
goto custom_mode_failed;
}
/*
* Ensure the new output state can be applied on
* its own and inform the client when it can not.
@ -569,8 +610,7 @@ handle_output_manager_apply(struct wl_listener *listener, void *data)
bool config_is_good = verify_output_config_v1(config);
if (config_is_good) {
output_config_apply(server, config);
if (config_is_good && output_config_apply(server, config)) {
wlr_output_configuration_v1_send_succeeded(config);
} else {
wlr_output_configuration_v1_send_failed(config);

View file

@ -114,8 +114,9 @@ resistance_move_apply(struct view *view, double *x, double *y)
if (rc.window_edge_strength != 0) {
/* Find any relevant window edges encountered by this move */
edges_find_neighbors(&next_edges, view, target, NULL,
check_edge_window, /* use_pending */ false);
edges_find_neighbors(&next_edges,
view, target, NULL, check_edge_window,
/* use_pending */ false, /* ignore_hidden */ true);
}
/* If any "best" edges were encountered during this move, snap motion */
@ -143,8 +144,9 @@ resistance_resize_apply(struct view *view, struct wlr_box *new_geom)
if (rc.window_edge_strength != 0) {
/* Find any relevant window edges encountered by this move */
edges_find_neighbors(&next_edges, view, *new_geom, NULL,
check_edge_window, /* use_pending */ false);
edges_find_neighbors(&next_edges,
view, *new_geom, NULL, check_edge_window,
/* use_pending */ false, /* ignore_hidden */ true);
}
/* If any "best" edges were encountered during this move, snap motion */

View file

@ -91,6 +91,24 @@ get_category(struct wlr_input_device *device)
static void
configure_libinput(struct wlr_input_device *wlr_input_device)
{
/*
* TODO: We do not check any return values for the various
* libinput_device_config_*_set_*() calls. It would
* be nice if we could inform the users via log file
* that some libinput setting could not be applied.
*
* TODO: We are currently using int32_t with -1 as default
* to desribe the not-configured state. This is not
* really optimal as we can't properly deal with
* enum values that are 0. After some discussion via
* IRC the best way forward seem to be to use a
* uint32_t instead and UINT32_MAX as indicator for
* a not-configured state. This allows to properly
* test the enum being a member of a bitset via
* mask & value == value. All libinput enums are
* way below UINT32_MAX.
*/
if (!wlr_input_device) {
wlr_log(WLR_ERROR, "no wlr_input_device");
return;
@ -189,6 +207,36 @@ configure_libinput(struct wlr_input_device *wlr_input_device)
wlr_log(WLR_INFO, "dwt configured");
libinput_device_config_dwt_set_enabled(libinput_dev, dc->dwt);
}
if ((dc->click_method != LIBINPUT_CONFIG_CLICK_METHOD_NONE
&& (libinput_device_config_click_get_methods(libinput_dev)
& dc->click_method) == 0)
|| dc->click_method < 0) {
wlr_log(WLR_INFO, "click method not configured");
} else {
wlr_log(WLR_INFO, "click method configured");
/*
* Note, the documentation claims that:
* > [...] The device may require changing to a neutral state
* > first before activating the new method.
*
* However, just setting the method seems to work without
* issues.
*/
libinput_device_config_click_set_method(libinput_dev, dc->click_method);
}
if ((dc->send_events_mode != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
&& (libinput_device_config_send_events_get_modes(libinput_dev)
& dc->send_events_mode) == 0)
|| dc->send_events_mode < 0) {
wlr_log(WLR_INFO, "send events mode not configured");
} else {
wlr_log(WLR_INFO, "send events mode configured");
libinput_device_config_send_events_set_mode(libinput_dev, dc->send_events_mode);
}
}
static struct wlr_output *

View file

@ -17,6 +17,7 @@
#include <wlr/types/wlr_viewporter.h>
#if HAVE_XWAYLAND
#include <wlr/xwayland.h>
#include "xwayland-shell-v1-protocol.h"
#endif
#include "drm-lease-v1-protocol.h"
#include "config/rcxml.h"
@ -172,9 +173,11 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo
(void)iface; (void)server;
#if HAVE_XWAYLAND
struct wl_client *xwayland_client =
server->xwayland ? server->xwayland->server->client : NULL;
if (xwayland_client && client == xwayland_client) {
struct wl_client *xwayland_client = (server->xwayland && server->xwayland->server)
? server->xwayland->server->client
: NULL;
if (client == xwayland_client) {
/*
* Filter out wp_drm_lease_device_v1 for now as it is resulting in
* issues with Xwayland applications lagging over time.
@ -184,6 +187,9 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo
if (!strcmp(iface->name, wp_drm_lease_device_v1_interface.name)) {
return false;
}
} else if (!strcmp(iface->name, xwayland_shell_v1_interface.name)) {
/* Filter out the xwayland shell for usual clients */
return false;
}
#endif

View file

@ -121,8 +121,9 @@ snap_move_to_edge(struct view *view, enum view_edge direction,
struct border next_edges;
edges_initialize(&next_edges);
edges_find_neighbors(&next_edges, view, target,
output, check_edge, /* use_pending */ true);
edges_find_neighbors(&next_edges,
view, target, output, check_edge,
/* use_pending */ true, /* ignore_hidden */ false);
/* If any "best" edges were encountered, limit motion */
edges_adjust_move_coords(view, next_edges,
@ -196,8 +197,9 @@ snap_grow_to_next_edge(struct view *view, enum view_edge direction,
edges_initialize(&next_edges);
/* Limit motion to any intervening edge of other views on this output */
edges_find_neighbors(&next_edges, view, *geo,
output, check_edge, /* use_pending */ true);
edges_find_neighbors(&next_edges,
view, *geo, output, check_edge,
/* use_pending */ true, /* ignore_hidden */ false);
edges_adjust_resize_geom(view, next_edges,
resize_edges, geo, /* use_pending */ true);
}
@ -255,8 +257,9 @@ snap_shrink_to_next_edge(struct view *view, enum view_edge direction,
view->output, check_edge, /* use_pending */ true);
/* Limit motion to any intervening edge of ther views on this output */
edges_find_neighbors(&next_edges, view, *geo,
view->output, check_edge, /* use_pending */ true);
edges_find_neighbors(&next_edges,
view, *geo, view->output, check_edge,
/* use_pending */ true, /* ignore_hidden */ false);
edges_adjust_resize_geom(view, next_edges,
resize_edges, geo, /* use_pending */ true);

View file

@ -2,6 +2,7 @@
#include <assert.h>
#include <stdio.h>
#include <strings.h>
#include <wlr/types/wlr_output_layout.h>
#include "common/macros.h"
#include "common/match.h"
#include "common/mem.h"
@ -1519,8 +1520,44 @@ view_on_output_destroy(struct view *view)
view->output = NULL;
}
static enum wlr_direction
opposite_direction(enum wlr_direction direction)
{
switch (direction) {
case WLR_DIRECTION_RIGHT:
return WLR_DIRECTION_LEFT;
case WLR_DIRECTION_LEFT:
return WLR_DIRECTION_RIGHT;
case WLR_DIRECTION_DOWN:
return WLR_DIRECTION_UP;
case WLR_DIRECTION_UP:
return WLR_DIRECTION_DOWN;
default:
return 0;
}
}
static enum wlr_direction
get_wlr_direction(enum view_edge edge)
{
switch (edge) {
case VIEW_EDGE_LEFT:
return WLR_DIRECTION_LEFT;
case VIEW_EDGE_RIGHT:
return WLR_DIRECTION_RIGHT;
case VIEW_EDGE_UP:
return WLR_DIRECTION_UP;
case VIEW_EDGE_DOWN:
return WLR_DIRECTION_DOWN;
case VIEW_EDGE_CENTER:
case VIEW_EDGE_INVALID:
default:
return 0;
}
}
struct output *
view_get_adjacent_output(struct view *view, enum view_edge edge)
view_get_adjacent_output(struct view *view, enum view_edge edge, bool wrap)
{
assert(view);
struct output *output = view->output;
@ -1530,32 +1567,31 @@ view_get_adjacent_output(struct view *view, enum view_edge edge)
return NULL;
}
struct wlr_box box = output_usable_area_in_layout_coords(output);
int lx = box.x + box.width / 2;
int ly = box.y + box.height / 2;
/* Determine any adjacent output in the appropriate direction */
struct wlr_output *new_output = NULL;
struct wlr_output *current_output = output->wlr_output;
struct wlr_output_layout *layout = view->server->output_layout;
switch (edge) {
case VIEW_EDGE_LEFT:
new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_LEFT, current_output, 1, 0);
break;
case VIEW_EDGE_RIGHT:
new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_RIGHT, current_output, 1, 0);
break;
case VIEW_EDGE_UP:
new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_UP, current_output, 0, 1);
break;
case VIEW_EDGE_DOWN:
new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_DOWN, current_output, 0, 1);
break;
default:
break;
enum wlr_direction direction = get_wlr_direction(edge);
new_output = wlr_output_layout_adjacent_output(layout, direction,
current_output, lx, ly);
/*
* Optionally wrap around from top-to-bottom or left-to-right, and vice
* versa.
*/
if (wrap && !new_output) {
new_output = wlr_output_layout_farthest_output(layout,
opposite_direction(direction), current_output, lx, ly);
}
/* When "adjacent" output is the same as the original, there is no adjacent */
/*
* When "adjacent" output is the same as the original, there is no
* adjacent
*/
if (!new_output || new_output == current_output) {
return NULL;
}
@ -1629,7 +1665,8 @@ view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_wind
}
/* Otherwise, move to edge of next adjacent display, if possible */
struct output *output = view_get_adjacent_output(view, direction);
struct output *output =
view_get_adjacent_output(view, direction, /* wrap */ false);
if (!output) {
return;
}
@ -1788,7 +1825,7 @@ view_snap_to_edge(struct view *view, enum view_edge edge,
if (across_outputs && view->tiled == edge && view->maximized == VIEW_AXIS_NONE) {
/* We are already tiled for this edge; try to switch outputs */
output = view_get_adjacent_output(view, edge);
output = view_get_adjacent_output(view, edge, /* wrap */ false);
if (!output) {
/*

View file

@ -16,15 +16,17 @@ struct xdg_popup {
struct view *parent_view;
struct wlr_xdg_popup *wlr_popup;
struct wl_listener commit;
struct wl_listener destroy;
struct wl_listener new_popup;
};
static void
popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup)
popup_unconstrain(struct xdg_popup *popup)
{
struct view *view = popup->parent_view;
struct server *server = view->server;
struct wlr_box *popup_box = &popup->current.geometry;
struct wlr_box *popup_box = &popup->wlr_popup->current.geometry;
struct wlr_output_layout *output_layout = server->output_layout;
struct wlr_output *wlr_output = wlr_output_layout_output_at(
output_layout, view->current.x + popup_box->x,
@ -39,7 +41,7 @@ popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup)
.width = output_box.width,
.height = output_box.height,
};
wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &output_toplevel_box);
}
static void
@ -48,9 +50,28 @@ handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
struct xdg_popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
/* Usually already removed unless there was no commit at all */
if (popup->commit.notify) {
wl_list_remove(&popup->commit.link);
}
free(popup);
}
static void
handle_xdg_popup_commit(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, commit);
if (popup->wlr_popup->base->initial_commit) {
popup_unconstrain(popup);
/* Prevent getting called over and over again */
wl_list_remove(&popup->commit.link);
popup->commit.notify = NULL;
}
}
static void
popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
{
@ -75,9 +96,13 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
popup->destroy.notify = handle_xdg_popup_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
popup->new_popup.notify = popup_handle_new_xdg_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
popup->commit.notify = handle_xdg_popup_commit;
wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
/*
* We must add xdg popups to the scene graph so they get rendered. The
* wlroots scene graph provides a helper for this, but to use it we must
@ -100,6 +125,4 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base);
node_descriptor_create(wlr_popup->base->surface->data,
LAB_NODE_DESC_XDG_POPUP, view);
popup_unconstrain(view, wlr_popup);
}