rebase: Sway 1.8 (#78)

* build: bump wlroots dependency to 0.16.0

* swaymsg: replace if with switch in pretty_print

* swaymsg: add GET_TREE pretty-printing

* swaybar: fix errno handling in status_handle_readable

If getline fails once, it was not reset before the next getline
call. errno is only overwritten by getline on error.

* Add cairo_image_surface_create error handling

cairo_image_surface_create can fail, e.g. when running out of
memory or when the size is too big. Avoid crashing in this case.

Closes: https://github.com/swaywm/sway/issues/6531

* build: bump version to 1.8-dev

Historically we've been sticking with the last release number in
the master branch. However that's a bit confusing, people can't
easily figure out whether they're using a release or a work-in-progress
snapshot. Only the commit hash appended to the version number may
help, but that's not very explicit and disappears when using a
tarball.

We could bump the version in master to the next release number.
However during the RC cycle there would be a downgrade from 1.8 to
1.8-rc1. Also it would be hard to tell the difference between a
stable release and an old snapshot.

This patch introduces a new pre-release identifier, "dev". It's
alphabetically before "rc" so it should be correctly sorted by
semver comparisons. "dev" is upgraded to "rc" (and then to stable)
when doing a release. The master branch always uses a "dev"
version, only release branches use "rc" or stable versions.

* [IPC] Add repeat delay/rate info to keyboard

Closes #6735

wlroots already has the info in the struct so let's access it and print it out.

* input/seat: unset has_focus when focus_stack becomes empty

We currently track the focus of a seat in two ways: we use a list called
focus_stack to track the order in which nodes have been focused, with
the first node representing what's currently focused, and we use a
variable called has_focus to indicate whether anything has focus--i.e.
whether we should actually treat that first node as focused at any given
time.

In a number of places, we treat has_focus as implying that a focused
node exists. If it's true, we attempt to dereference the return value of
seat_get_focus(), our helper function for getting the first node in
focus_list, with no further checks. But this isn't quite correct with
the current implementation of seat_get_focus(): not only does it return
NULL when has_focus is false, it also returns NULL when focus_stack
contains no items.

In most cases, focus_stack never becomes empty and so this doesn't
matter at all. Since focus_stack stores a history of focused nodes, we
rarely remove nodes from it. The exception to this is when a node itself
goes away. In that case, we call seat_node_destroy() to remove it from
focus_stack and free it. But we don't unset has_focus if we've removed
the final node! This lets us get into a state where has_focus is true
but seat_get_focus() returns NULL, leading to a segfault when we try to
dereference it.

Fix the issue both by updating has_focus in seat_node_destroy() and by
adding an assertion in seat_get_focus() that ensures focus_stack and
has_focus are in sync, which will make it easier to track down similar
issues in the future.

Fixes #6395.

[1] There's some discussion in #1585 from when this was implemented
about whether has_focus is actually necessary; it's possible we could
remove it entirely, but for the moment this is the architecture we have.

* swaybar: fix tray_padding vs min-height re: scale

Co-authored-by: xdavidwu <xdavidwuph@gmail.com>

* swaybar: fix tray item icon scaling, positioning

* container: Fix crash when view unmaps + maps quickly

Followup on 4e4898e90f.

If a view quickly maps and unmaps repeatedly, there will be multiple
destroyed containers with same view in a single transaction. Each of
these containers will then try to destroy this view, resulting in use
after free.
The container should only destroy the view if the view still belongs
to the container.

Simple reproducer: couple XMapWindow + XUnmapWindow in a loop followed
by XDestroyWindow.

See #6605

* commands/move: Fix crash when pos_y is omitted

Fixes #6737

* Destroy sub-surfaces with parent layer-shell surface

Closes: https://github.com/swaywm/sway/issues/6337

* Add safety assert in parse_movement_unit

Let's add this just in case a caller passes argc == 0.

References: https://github.com/swaywm/sway/issues/6737#issuecomment-1008082540

* meson: check: false on run_command

Future meson releases will change the default and warns when the
implicit default is used, breaking builds.

Explicitly set check: false to maintain behavior and silence warnings.

* Print deprecation notice when running SUID

SUID privilege drop is needed for the "builtin"-backend of libseat,
which copied our old "direct" backend behavior for the sake of
compatibility and ease of transition.

libseat now has a better alternative in the form of seatd-launch. It
uses the normal seatd daemon and libseat backend and takes care of SUID
for us.

Add a soft deprecation warning to highlight our future intent of
removing this code. The deprecation cycle is needed to avoid surprises
when sway no longer drops privileges.

* xdg-shell: use toplevel geometry to adjust the popup box

`popup_unconstrain` uses view coordinates to init the output box for
popups. However wlroots expects the box to be set in a toplevel surface
coordinate system, which is not always equal to view. The difference
between those is a window geometry set via xdg-shell.

GTK4 reserves some space for client-side decoration and thus has a
window with top left corner not matching to (0, 0) of a surface. The box
calculated without taking that into account was slightly shifted
compared to the actual output and allowed to position part of the popup
off screen.

* build: fix building with basu

02b412a introduced the use of list for sdbus deps, however
it was assuming that all packages which were in a list has a version
higher than 239. That is true for libsystemd and libelogind, since they
use the same versions, however basu is using version numbers which are
way lower than what libsystemd/libelogind are using, so basu only build
is failing.

* Upgrade for wlroots surface refactoring

See [1] for details.

[1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3412

* commands/focus: drop trailing whitespace

* input/cursor: count pointer gestures as idle activity

Fixes https://github.com/swaywm/sway/issues/6765.

* input/cursor: treat swipe begin as idle activity too

Accidentally overlooked in fd53f80.

* treat fullscreen windows as 'tiled' for commands/focus

* transaction: destroying nodes aren't hidden

Commit 37d7bc6998 ("transaction: Only wait for ack from visible
views") introduced a check which uses view_is_visible() to check if a view
is still visible on the screen. However view_is_visible() will early
return in case the node is in the destroying state. This is incorrect
for transactions, since a destroying view which is visible will trigger
configure events for other clients. This bug was visible when repeatedly
opening and closing two views side by side, since we ignore the
destroying node we get a frame where the still open view is shown with
the old configure values and the rest is the desktop background. The
next frame is than correct again.

Fix this by considering destroying views as visible, we correctly wait
for them and send the configure events to other views in time, fixing
the background flicker.

Fixes #6473

* build: execute wlroots subproject before finding deps

wlroots often requires dependencies more recent than Sway's.
Executing the wlroots subproject first will give Meson a chance to
find these newer dependencies, possibly via subprojects.

The subproject will override the "wlroots" dependency when executed,
so we don't need to use get_variable anymore.

References: https://github.com/swaywm/sway/pull/6498#issuecomment-1001746017

* tray: do not render passive items

https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/#org.freedesktop.statusnotifieritem.status

* cmd/swap: error on swapping a container with itself

* input/cursor: pass through pointer hold gestures

This just follows swaywm/wlroots#3047, so `wl_pointer_gestures_v1`
clients can be notified of these events.

* swaynag: remove buffer destruction condition

An address of a variable can never be NULL, so checking it doesn't make
sense; and `destroy_buffer()` can operate on already destroyed buffers
anyway.

Fixes #6780

* Use bools for CLI flags

* xwayland: listen to `request_activate` event

When REAPER submenu is closed `XCB_CLIENT_MESSAGE` with type
`NET_ACTIVE_WINDOW` is sent to set focus to parent menu.

Closes: https://github.com/swaywm/sway/issues/6324

* chore: chase wlr_output_layout_get_box() update

https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3439

* Chase wlroots xdg-shell refactor

* Minor update to focus_on_window_activation

Removed xwayland limitation since wayland clients are supported via xdg-activation.

* Translated README into Italian

* readme: add link to Italian translation

* readme: sort language list alphabetically

* readme: use relative links for translations

* xdg-shell: use wlr_xdg_toplevel in sway_view

Improved type safety.

Closes: https://github.com/swaywm/sway/issues/6813

* xdg-shell: use wlr_xdg_popup in sway_xdg_popup

Improved type safety.

* Fix snprintf compiler warning

* Remove all sprintf calls

Replace them with snprintf, which ensures buffer overflows won't
happen.

* sway/commands: add missing wlr_keyboard interface include in xkb_switch_layout

* sway/input: use wlr_input_device from input device base

* Remove some erroneous apostrophes in comments

* Don't enter seatop_move_floating when fullscreen

Currently, a floating window that's been fullscreened can send us
xdg_toplevel::move, and we'll enter seatop_move_floating, which lets us
drag the surface around while it's fullscreen. We don't want
this--fullscreen surfaces should always be aligned to the screen--so add
the same check that seatop_default already does when entering this mode.

Tested with Weston's weston-fullscreen demo, which sends a move request
if you click anywhere on its surface.

* swaynag: die on all allocation failures

* sway/input: don't pass possibly invalid modifiers pointer

active_keyboard may be NULL, in which case an invalid pointer could be
passed to wlr_input_method_keyboard_grab_v2_send_modifiers. This
procedure call is unnecessary since wlroots commit 372a52ec "input
method: send modifiers in set_keyboard", so the call can simply be
removed.

Fixes #6836.

* sway/input: destroy sway_switch properly

Fix: #6861
Added seat_device_destroy function to seat_device_destroy function.

* commands/focus: fix segfault when no container is already focused.

Fixes #6690.

* Remove WLR_SWITCH_STATE_TOGGLE usage

Ref [1].

[1]: 4792446ee8

* Replace pcre with pcre2

Closes: https://github.com/swaywm/sway/issues/6838

* swaybar: remove swaybar_output.input_region

No need to keep the region around, we can immediately destroy it
after the wl_surface.set_input_region request.

* swaybar: set opaque region

When the background color is fully opaque, set the surface's opaque
region to the whole surface.

* Updating criteria checking with PCRE2

* swaynag: allocate button_details with details

They are used together, so it doesn't make sense to allocate them
separately.

* swaynag: statically allocate button_close, and move declaration

Every swaynag has a close button, so it doesn't make sense to
allocate it dynamically. The declaration is moved later to when
it is actually needed.

* swaynag: remove unnecessary zero of swaynag struct

Global variables are initialized to 0.

* swaynag: remove redundant status variables in main

Instead, we just use `status` for all failures.

* remove unnecessary strlen call

* sway/input/cursor: take device mm size from wlr_tablet

* sway/input/seat: take output name from specialized input device

* sway/input: follow up wlroots input device events renaming

* sway/input: fix bad position of wlr_drag

* sway/input: wlr_seat_keyboard() now takes wlr_keyboard

* bash-completion: localize variables

* sway/main: move constants off the stack

This makes stack traces from gdb slightly easier to read.

* Fix farsi label

* Avoid format-truncation warning

The existing code gives this error when compiled with GCC 12:

../sway/server.c: In function ‘server_init’:
../sway/server.c:217:75: error: ‘%d’ directive output may be truncated writing between 1 and 11 bytes into a region of size 8 [-Werror=format-truncation=]
  217 |                 snprintf(name_candidate, sizeof(name_candidate), "wayland-%d", i);
      |                                                                           ^~
../sway/server.c:217:66: note: directive argument in the range [-2147483647, 32]
  217 |                 snprintf(name_candidate, sizeof(name_candidate), "wayland-%d", i);
      |                                                                  ^~~~~~~~~~~~
../sway/server.c:217:17: note: ‘snprintf’ output between 10 and 20 bytes into a destination of size 16
  217 |                 snprintf(name_candidate, sizeof(name_candidate), "wayland-%d", i);
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Because i is never negative, this is a false positive, but it is easy to
change i to unsigned to silence the error.

* Shuffle variables to satisfy -Werror=restrict

This also fixes an invalid strlen invocation on uninitialized memory.

* layer_shell: keep output non-NULL wherever possible

Our layer shell implementation assigns every layer surface to an output
on creation. It tracks this output using the output field on the
underlying wlr_layer_surface_v1 structure. As such, much of the existing
code assumes that output is always non-NULL and omits NULL checks
accordingly.

However, there are currently two cases where we destroy a
sway_layer_surface and output is NULL. The first is when we can't find
an output to assign the surface to and destroy it immediately after
creation. The second is when we destroy a surface in response to its
output getting destroyed, as we set output to NULL in
handle_output_destroy() before we call wlr_layer_surface_v1_destroy(),
which is what calls the appropriate unmap and destroy callbacks.

The former case doesn't cause any problems, since we haven't even
allocated a sway_layer_surface at that point or registered any
callbacks. The latter case, however, currently triggers a crash (#6120)
if a popup is visible, since our popup_handle_unmap() implementation
can't handle a NULL output.

To fix this issue, keep output set until right before we free the
sway_layer_surface. All we need to do is remove some of the cleanup
logic from handle_output_destroy(), since as of commit c9060bcc12
("layer-shell: replace close() with destroy()") that same logic is
guaranteed to be happen later when wlroots calls handle_destroy() as
part of wlr_layer_surface_v1_destroy().

This lets us remove some NULL checks from other unmap/destroy callbacks,
which is nice. We also don't need to check that the wlr_output points to
a valid sway_output anymore, since we unset that pointer after disabling
the output as of commit a0bbe67076 ("Address emersions comments on
output re-enabling") Just to be safe, I've added assertions that the
wlr_output is non-NULL wherever we use it.

Fixes #6120.

* Chase wlroots X11 hints update

* Add Swedish README

* Support cursor capture in grimshot

Refactor argument parser

Bring back `sh` compatibility

Default to NOTIFY=no

* Update grimshot.1.scd

Fixed typo. The object is **files**, which is plural. **image** modifies files; it's not countable.

* xkb_switch_layout: fix relative layout switches

Fixes #6011

* Implement ext-session-lock-v1

* Avoid inspecting a NULL view in seat_set_focus

Fixes #6968

* swaynag: do error checking and rename read_from_stdin

read_from_stdin not only read from stdin, but trimming trailing
newlines, so rename it to reflect this.

* swaynag: improve robustness when loading config

* swaynag: combine consecutive declaration/assignments

* config: Remove unused mouse binding structure

Mouse bindings are handled alongside normal bindings. Remove the unused
separate data structure definition to avoid confusion.

Signed-off-by: Michael Weiser <michael.weiser@gmx.de>

* Replace strncpy with memcpy

strncpy is useless here, is dangerous because it doesn't guarantee
that the string is NUL-terminated and causes the following warning:

    ../sway/criteria.c: In function ‘criteria_parse’:
    ../sway/criteria.c:712:25: error: ‘strncpy’ destination unchanged after copying no bytes [-Werror=stringop-truncation]
      712 |                         strncpy(value, valuestart, head - valuestart);
          |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Add descriptions for `stacking` and `tabbed` layouts

Resolves #5918

* man: Fix trailing spaces

* server: request xdg-shell v2

Wlroots does not yet support the newer xdg-shell versions and now
requires the compositor to set the supported xdg-shell version during
creation. Set this to v2 for sway as well.

Fixes https://github.com/swaywm/sway/issues/7001

* realtime: request SCHED_RR using CAP_SYS_NICE

Try to gain SCHED_RR (round-robin) realtime scheduling privileges before
starting the server. This requires CAP_SYS_NICE on Linux systems.
We additionally register a pthread_atfork callback which resets the
scheduling class back to SCHED_OTHER (the Linux system default).

Due to CAP_SYS_NICE, setting RLIMIT_RTPRIO has no effect on the process
as documented within man 7 sched (from Linux):

  Privileged (CAP_SYS_NICE) threads ignore the RLIMIT_RTPRIO limit;
  as with older kernels, they can make arbitrary changes to
  scheduling policy and priority. See getrlimit(2) for further
  information on RLIMIT_RTPRIO

Note that this requires the sway distribution packagers to set the
CAP_SYS_NICE capability on the sway binary.

Supersedes #6992

* ext-session-lock: disable direct scan-out when locked

* Polish the language in README.zh-CN.md & sync with English one

Co-Authored-By: Urey. Xue <urey.s.knowledge@gmail.com>

* De-duplicate IPC output descriptions

* Handle NULL output make/model/serial

* chore: chase wlroots xdg-shell update

* xdg-shell: schedule a configure on maximize request

This commit reverts 03879290dbee26127f6867ef60bc2a7f9a6c8c5f and
fc84bcb7fb0ffa29b1f9bed287762241a3473803.

* Add a Hindi (हिन्दी) translation to the README

Hindi is one of the most prominent languages of the Indian Subcontinent.
This commit adds the translation of the README into the Hindi language.
Some of the words are still written in English because there wasn't an
appropriate technical term of the word in the language.

Co-authored-by: Surendrajat <surendrajat@protonmail.com>

* sway: add bindgesture command

Co-authored-by: Michael Weiser <michael.weiser@gmx.de>

* build: link with -pthread

Fixes the following FreeBSD error:

    ld: error: undefined symbol: pthread_getschedparam
    >>> referenced by realtime.c:25 (../sway/realtime.c:25)
    >>>               sway/sway.p/realtime.c.o:(set_rr_scheduling)

Fixes: a3a82efbf6b5 ("realtime: request SCHED_RR using CAP_SYS_NICE")

* ipc: remove chatty debug log messages

These aren't particularly useful, and clobber the debug logs.

* Refuse to start when SUID is detected

This ensures that those surprised by the deprecation of SUID operation
receive an error rather than accidentally having sway run as root.

This detection will be removed in a future release.

* swaynag: move close_button up to fix SIGSEGV

When swaynag_parse_options encounters '--dismiss-button' (or its
shorthand '-s'), it sets the text of the first button in the
swaynag.buttons list, which is expected to exist and to be the dismiss
button, to the one passed by the user.

Commit 4780afb68b4ee2cdf0e4925f40cf885819f8a74a ("swaynag: statically
allocate button_close, and move declaration") moved the list
initialization to after swaynag_parse_options is called which made that
code fail.

For example, the command 'swaynag --dismiss-button Dismiss' crashes and
'swaynag --message Message --button Yes "" --dismiss-button Dismiss'
shows the wrong buttons.

Move it back to before swaynag_parse_options is called.

* config/output: use wlr_output_commit_state

This makes the code more robust because we don't potentially leave
bad state in wlr_output.pending behind anymore. This also fixes a
bug.

Closes: https://github.com/swaywm/sway/issues/7043
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3610

* Allocate enough space for `cmd_results->error`

* Remove access to wlr_input_device union

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3626
Closes: https://github.com/swaywm/sway/issues/7077

* Rename dpms output command to power

The "dpms" command refers to VESA Display Power Management
Signaling, a deprecated standard. It's superseded by VESA DPM.

Instead of tying out command name to a particular standard, use the
neutral term "power".

* Strip quotes in bindsym --input-device=...

If the input device is quoted, which is common when using variables in the
config file, those quotes must be ignored here, or the input device will be
ignored.

Fixes #7029.

* Avoid unecessary string copy

* Reject font values that are invalid for pango

Use pango to parse font configuration early, and reject the command as
invalid if the value is invalid for pango. Since we're already parsing
the font into a `PangoFontDescription`, keep that instance around and
avoid re-parsing the font each time we render text.

Fixes: https://github.com/swaywm/sway/issues/6805

* Reuse parsed PangoFontDescription

Avoids parsing the configured font each time text is rendered.

* ipc: add "power" to output reply

* config.in: switch to `output power`

* Remove internal references to DPMS

While at it, use an int for the config field, just like we do for
all other fields.

* fix: remove redundant empty statement in main.c

This semi-colon looks like a typo. Luckily, it has no effect on the code as it's treated as an empty statement leading the switch case.

Really straightforward nitpick change, was just something I was confused by when reading over the code.

* input: chase delta_discrete semantics change

* swaymsg: fix floating_nodes being ignored

Fix floating_nodes being ignored in pretty_print_tree.

* ipc: make get_deco_rect check config->hide_lone_tab

Without this, the `IPC_GET_TREE` ipc call would return false information
about the container's `deco_rect` and `rect` properties if
`hide_edge_borders --i3` was in effect.

* grimshot: fix tilde expansion within quotes

* Enable single-pixel-buffer-v1

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3428

* sway-output.5: improve display of parameter

Since "width" and "height" are separate parameters, show them as such.

* man: sway(5) move fixes

* ipc: drop WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN case

This has been removed from wlroots.

* config/output: test adaptive sync

Required for [1].

[1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3637

* Fix crash in xdg_activation_v1.c

wlr_xdg_surface_from_wlr_surface() can return a NULL pointer, so check for NULL before dereferencing it.

* sway: Add non-desktop-output type

Currently, when encountering a non-desktop display, sway offers the
output for leasing and returns without storing it in a sway specific
output type like `struct sway_output`.  Additionally, running
`swaymsg -t get_outputs` doesn't show non-desktop outputs.

This commit stores the non-desktop outputs into a struct called
`sway_output_non_desktop`, and adds them to a list on `sway_root`

* sway: add non-desktop outputs to json when running `swaymsg -t get_outputs`

* swaymsg: show non-desktop property when pretty printing outputs

* man: Add XWayland information

* ipc: expose mode picture aspect ratio

* swaymsg: show mode picture aspect ratio

* build: simplify protocol paths

No need for arrays here.

* sway/commands/output: Add command for unplugging non-physical outputs

* Improve Japanese translation

* allow pointer_constraints on layer_shell surfaces

* check for NULL

* use seat directly

* Use keyboard_state.focused_surface directly

* input: focus floating container when clicked on border

Fixes #7209.

* input: focus container when scrolling on titlebar

Fixes #6503.

* Fix leaks in criteria_destroy()

* Avoid double free in criteria_destroy()

* Add support for ext-idle-notify-v1

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3753

* input: tweak focus behavior to allow focusing parent containers

Sway focuses the inactive child when focusing split containers. However,
there is currently no way to focus the parent container itself by mouse.
A user must use the keyboard to do so.

This commit maintains the current behavior, but makes it such that a
second click on the split container titlebar (i.e., after its children
are visible) focuses the split container itself.

* Fix keymap being NULL and segfaulting on dev add

Moved `libinput_config` to the callers of
`sway_input_configure_libinput_device` so that we send the event after
the added event.

* Rework session lock keyboard focus handling

When removing outputs, it is possible to end up in a situation where
none of the session lock client's surfaces have keyboard focus,
resulting in it not receiving keyboard events.  Track the focused
surface and update it as needed on surface destroy.

* Fix focus tracking when session lock is active

Remove the incorrect attempt to block focus changes when an input grab
is present and replace it with the same logic used for layer_shell-based
screen lockers: restore the focus after changing it.

This fixes a use-after-free of seat->workspace if outputs are destroyed
while a screen lock is enabled.

* container_floating_set_default_size: Store workspace size box on the stack

* Support libinput's 1.21 new dwtp option

Support the new dwtp (disable while trackpointing) option introduced in
libinput 1.21, allowing users to control whether the trackpoint (like
those in Thinkpads, but not only) should be disabled while using the
keyboard/touchpad.

See: https://gitlab.freedesktop.org/libinput/libinput/-/issues/731

* tree: support formatting null titles

Any windows that have never had a title set visually behave closer to
that of an empty title, but are unformattable, as the code bails out
early on a NULL title.

* criteria: allow matching on empty (NULL) titles

* criteria: allow matching for NULL string criteria

* ci: install hwdata

* Use wl_signal_emit_mutable()

This function fixes segfaults when emitting a signal potentially
removes arbitrary listeners.

* Use wlr_damage_ring

wlr_output_damage is to be replaced with wlr_damage_ring, so use that.

* lock: fix crash on output destroy

Closes: https://github.com/swaywm/sway/issues/7120

* container_get_siblings: handle NULL workspace

* ci: checkout wlroots 0.16.0

* workspace_create: Don't allow NULL name

(cherry picked from commit 34933bb84350fe805d82276ea02d5732546e9993)

* output: set damage ring bounds to pixel values

Fixes: https://github.com/swaywm/sway/issues/7254
(cherry picked from commit 85005b52fe5b832e4ea914fa28048b0c5c803769)

* Use shm_open instead of mkstemp

shm_open is more reliable because it does not require
a writeable filesystem folder, unlike mkstemp.

(cherry picked from commit e2bc8866f46701e9c825ad7fa5baac02b2e4898f)

* build: drop wayland-scanner fallback

(cherry picked from commit 366f6ef3d31688631dc453028e108f98a1d7ab57)

* build: unify server & client protocol generation

No need to make a difference here, let's just generate header
files for both.

(cherry picked from commit 5be5a038da8a3789a19945719f2a27233291445d)

* build: drop "server" from target name for protocol code

(cherry picked from commit e5475d9310941ce88ed016ce1515b36e3a440252)

* build: drop intermediate libraries for protocols

(cherry picked from commit af8a5a8918ef42336194fb1077b008a736de7af9)

* root: move the workspace matching code to its own file

This removes the pid_workspace bits from tree/root before it gets
too interesting.

No functional change.

(cherry picked from commit eb5021ef990fb29ff86544aea58d687ad62c757a)

* node: prettify node type names

(cherry picked from commit 1c4b94ae3ca94b972410c80a61404a347af1ee68)

* launcher: track workspaces by node

This removes the need to rename the pid_workspaces when a workspace
is renamed.

It also opens the possibility of tracking other node types. Tracking
containers would allow application to be placed correctly in the
container tree even if the user has moved their focus elsewhere since
it was launched.

(cherry picked from commit 3b49f2782e8faf68766269b9c7390b16e25ae824)

* launcher: use xdga tokens

This reuses wlroots token tracking for workspace matching. It doesn't
export any xdga tokens for clients yet.

(cherry picked from commit bd66f4943da1c96edc3ba49573e27b42b688c543)

* launcher: rename pid_workspace to launcher_ctx

Soon we will match views with more than just a pid.

(cherry picked from commit d75c9f9722389d441fd24bd490c5cf12c4bef39a)

* view: associate launch contexts with views

Views now maintain a reference to a launch context which, as a last
resort, is populated at map time with a context associated with its pid.
This opens the possibility of populating it before map via another
source, e.g. xdga-tokens or configuration.

(cherry picked from commit 864b3a9a18f236f92f1898bb44ab977ceaebfd68)

* launcher: initialize launcher_ctxs once on startup

(cherry picked from commit 66568508c06267445489d655c91c94a34d6d9ffe)

* launcher: fudge the interface a bit

We want to create a context before knowing the pid it will match with.

(cherry picked from commit bdeb9f95651f6c99cc2f4cfb59020ddee202cf36)

* launcher: export xdga tokens and use them for workspace matching

(cherry picked from commit 30ad4dc4a5a41ce7c7aa85096a6e18f374172983)

* launcher: export X startup ids and use them for workspace matching

(cherry picked from commit 28fda4c0d38907fab94dc7d82c9dcf0754748b4e)

* swaybar: Prioritize hotspot events to bar bindings

This is consistent with i3bar's behaviour, and for example, allows binding a
command to button1, while still being able to click on tray icons or other
zones on the bar's status line which may have their own bindings.

E.g., in Sway, without this commit, this config. makes tray icons unclickable:

    bar {
        # ...
        bindsym button1 exec swaynag -m You_clicked_the_tray._Want_some_help?
    }

But the same configuration in i3 (with i3-nagbar) keeps tray items clickable.

Signed-off-by: Joan Bruguera <joanbrugueram@gmail.com>
(cherry picked from commit 53f9dbd424dc173a85c9f4cd30802259d38b1ef4)

* swaybar: Make hotspots block bar release bindings

The previous commit prioritized hotspots before bar bindings for press events,
which matches i3's behaviour. However, since hotspots don't need to do any
processing on release events, those were not handled, and simply fell through
to `bindsym --release` bar bindings (if any).

This is counter-intuitive, and doesn't match i3's behaviour. Instead in case
a hotspot handles the press event, it should also handle the release event,
doing nothing, but blocking the event from triggering a --release bar binding.

E.g., in Sway, without this commit, this config. shows a text on tray clicks:

    bar {
        # ...
        bindsym --release button1 exec swaynag -m I_got_the_release_event.
    }

But the same configuration in i3 (with i3-nagbar) doesn't show the text.

Signed-off-by: Joan Bruguera <joanbrugueram@gmail.com>
(cherry picked from commit 94b69acf0d7b26ee5af2172300cb18473508da76)

* build: drop unused wayland-egl dependency

(cherry picked from commit 37e4a3d6370dc6ba2b0877d588845c06781e880e)

* build: bump version to 1.8-rc1

* Fix build on Debian Stable

(cherry picked from commit dca0bb5749bc16f91ab964fc1b06ebb9a453368f)

* build: fix have_xwayland when xcb-icccm is not found

xcb-icccm is required to build Xwayland support.

Backported from commit d41f11e6bd8cef80f02dda4c66d4a31611aed753.

* build: bump version to 1.8-rc2

* seat: Avoid sending redundant keymaps on reload

When we reload the config, we reset every input device and re-apply
configuration from the config file. This means that the keyboard keymap
is updated at least once during config reload, more if the config file
contains keyboard configuration.

When they keyboard keymap changes and is updated through wlr_seat, the
keymap ends up sent to every keyboard bound in every client, seemingly
multiple times. On an x230 of mine with a keyboard layout set in the
config file, I see 42 keymap events sent to foot on config reload.

Reduce events from keyboard configurations by skipping all but the
currently active keyboard for the seat, and by clearing the active
keyboard during input manager device reset. After this change, I only
see a single just-in-time keymap event.

Fixes: https://github.com/swaywm/sway/issues/6654

* criteria: be lenient on window_role and instance too

* build: bump version to 1.8-rc3

* commands/move: Warp cursor after moving workspace to another output

This makes sway's behavior consistent with i3 when `mouse_warping` is
set to any value besides `none`.

Fixes #7027.

(cherry picked from commit e3c63bf58d0744dfb436f0f38442ce3735e40f47)

* seat: Set keyboard if seat keyboard is NULL

sway sends wl_keyboard.enter on seat focus change and when a keyboard
active on a seat is configured. If all keyboards are removed and a
keyboard is added back without changing the focused client, no new
notify event would be sent despite having keyboard focus. This could
lead to key events without notify, which is a protocol violation.

As a quick fix, when configuring a keyboard on a seat where no keyboard
is currently active, activate the keyboard so that a focused surface
will receive a notify event.

Regressed by: e1b268af98edeb09e570e8855ef64f0719cbafe2
Closes: https://github.com/swaywm/sway/issues/7330

(cherry picked from commit 1ade0ce753dc5f588584f444ce80d27c3b1e4300)

* build: bump version to 1.8-rc4

* swaynag: fix NULL font description

The font description was only set if provided on the CLI. It was
left NULL for the defaults and when reading from the config file.

Closes: https://github.com/swaywm/sway/issues/7186
(cherry picked from commit fd0af78e43f4dd67a404f475c676b25ae38a4b82)

* build: bump version to 1.8

* Removed other README languages

* Fixed build issues

* Removed alpha from render_data struct

* Updated PKGBUILDs and COPR spec

* Update sway/desktop/render.c

Co-authored-by: Alexis Tacnet <alexistacnet@gmail.com>

* Fixed deco_data not being initialized properly

* Replaced wlr_egl_(make|unset)_current with eglMakeCurrent

* Added matrix_projection into fx_renderer

Signed-off-by: Michael Weiser <michael.weiser@gmx.de>
Co-authored-by: Sefa Eyeoglu <contact@scrumplex.net>
Co-authored-by: Simon Ser <contact@emersion.fr>
Co-authored-by: Seth Barberee <seth.barberee@gmail.com>
Co-authored-by: Thomas Hebb <tommyhebb@gmail.com>
Co-authored-by: Nathan Schulte <nmschulte@gmail.com>
Co-authored-by: xdavidwu <xdavidwuph@gmail.com>
Co-authored-by: David Rosca <nowrep@gmail.com>
Co-authored-by: David96 <david@hameipe.de>
Co-authored-by: Kenny Levinsen <kl@kl.wtf>
Co-authored-by: Aleksei Bavshin <alebastr89@gmail.com>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
Co-authored-by: Tudor Brindus <me@tbrindus.ca>
Co-authored-by: Patrick Hilhorst <git@hilhorst.be>
Co-authored-by: Rouven Czerwinski <rouven@czerwinskis.de>
Co-authored-by: Tobias Bengfort <tobias.bengfort@posteo.de>
Co-authored-by: Ronan Pigott <rpigott@berkeley.edu>
Co-authored-by: Kirill Primak <vyivel@eclair.cafe>
Co-authored-by: Tuomas Yrjölä <mail@yrhki.fi>
Co-authored-by: Kirill Primak <vyivel@posteo.net>
Co-authored-by: Alexander Browne <elcste@users.noreply.github.com>
Co-authored-by: Marco Rubin <20150305+Rubo3@users.noreply.github.com>
Co-authored-by: Muhamed Hobi <woohoomoo2u@gmail.com>
Co-authored-by: Simon Zeni <simon@bl4ckb0ne.ca>
Co-authored-by: Nihal Jere <nihal@nihaljere.xyz>
Co-authored-by: Alexander Gramiak <agrambot@gmail.com>
Co-authored-by: Moon Sungjoon <sumoon@seoulsaram.org>
Co-authored-by: Nicolas Avrutin <nicolas@avrutin.net>
Co-authored-by: ndren <andreien@ctemplar.com>
Co-authored-by: Bill Li <billli11hkb@gmail.com>
Co-authored-by: Leonardo Hernández Hernández <leohdz172@protonmail.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Manuel Stoeckl <code@mstoeckl.com>
Co-authored-by: Yasin Silavi <59373143+sttatusx@users.noreply.github.com>
Co-authored-by: Daniel De Graaf <code@danieldg.net>
Co-authored-by: kraftwerk28 <kefirchik3@gmail.com>
Co-authored-by: Eskil <67291226+eschillus@users.noreply.github.com>
Co-authored-by: Alice Carroll <git@alice-carroll.pet>
Co-authored-by: Alan <51193876+Pound-Hash@users.noreply.github.com>
Co-authored-by: Victor Makarov <vitja.makarov@gmail.com>
Co-authored-by: Michael Weiser <michael.weiser@gmx.de>
Co-authored-by: -k <slowdive@me.com>
Co-authored-by: Hongyi <61831273+FrozenArcher@users.noreply.github.com>
Co-authored-by: Urey. Xue <urey.s.knowledge@gmail.com>
Co-authored-by: LordRishav <75823494+LordRishav@users.noreply.github.com>
Co-authored-by: Surendrajat <surendrajat@protonmail.com>
Co-authored-by: Florian Franzen <Florian.Franzen@gmail.com>
Co-authored-by: Greg Depoire--Ferrer <greg@gregdf.com>
Co-authored-by: Thomas Jost <schnouki@schnouki.net>
Co-authored-by: Hugo Osvaldo Barrera <hugo@barrera.io>
Co-authored-by: zkldi <ktchidev@gmail.com>
Co-authored-by: llyyr <llyyr.public@gmail.com>
Co-authored-by: Baltazár Radics <baltazar.radics@gmail.com>
Co-authored-by: Martin Michlmayr <tbm@cyrius.com>
Co-authored-by: Filip Szczepański <jazz2rulez@gmail.com>
Co-authored-by: Alex Maese <memaese@hotmail.com>
Co-authored-by: マリウス <marius@xn--gckvb8fzb.com>
Co-authored-by: Andri Yngvason <andri@yngvason.is>
Co-authored-by: ohno418 <yutaro.ono.418@gmail.com>
Co-authored-by: Ferdinand Schober <ferdinand.schober@fau.de>
Co-authored-by: cparm <armelcadetpetit@gmail.com>
Co-authored-by: Yaroslav de la Peña Smirnov <yps@yaroslavps.com>
Co-authored-by: Alexander Orzechowski <orzechowski.alexander@gmail.com>
Co-authored-by: pudiva chip líquida <pudiva@skylittlesystem.org>
Co-authored-by: Puck Meerburg <puck@puckipedia.com>
Co-authored-by: Callum Andrew <calcium@mailbox.org>
Co-authored-by: Ronan Pigott <ronan@rjp.ie>
Co-authored-by: Joan Bruguera <joanbrugueram@gmail.com>
Co-authored-by: nerdopolis <bluescreen_avenger@verizon.net>
Co-authored-by: Ankit Pandey <anpandey@protonmail.com>
Co-authored-by: Alexis Tacnet <alexistacnet@gmail.com>
This commit is contained in:
Erik Reider 2023-01-04 23:32:43 +01:00 committed by GitHub
parent 0365cc5d50
commit 94ebb45ee3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
122 changed files with 3832 additions and 1576 deletions

View file

@ -1,7 +1,6 @@
# SwayFX: A Beautiful Sway Fork
![](assets/swayfx_screenshot.jpg)
![swayfx_screenshot](assets/swayfx_screenshot.jpg)
Sway is an incredible window manager, and certainly one of the most well established wayland window managers. However, it is restricted to only include the functionality that existed in i3. This fork ditches the simple wlr_renderer, and replaces it with our fx_renderer, capable of rendering with fancy GLES2 effects. This, along with a couple of minor changes, expands sway's featureset to include the following:
+ **Anti-aliased rounded corners, borders, and titlebars**
@ -51,7 +50,7 @@ Install dependencies:
* wlroots
* wayland
* wayland-protocols \*
* pcre
* pcre2
* json-c
* pango
* cairo
@ -78,9 +77,9 @@ SwayFX will drop root permissions shortly after startup.
SwayFX would love to receive any new features that you're willing to build! Generally, we'd like to focus on eye-candy type improvements to keep our scope appropriate. If you'd like to build something that you think may be out of that focus, please raise an issue and we can discuss whether or not it will fit within this project.
Here's a quick outline of where most of our changes lie vs the main sway repository:
+ `sway/desktop/render.c`: the file that handles calling `fx_renderer` to render to the screen, handles damage tracking and scaling
+ `sway/desktop/fx_renderer.c`: the meat and potatoes of this project, structured as similarly to wlr_renderer as possible
+ `sway/desktop/shaders`: where all of the shaders that fx_renderer uses live
Please join our (for the time being very small) Discord server for development chatter! https://discord.gg/jVjjFdcN

View file

@ -23,9 +23,9 @@ depends=(
"libxcb"
"libxkbcommon.so"
"pango"
"pcre"
"pcre2"
"ttf-font"
"wlroots<0.16"
"wlroots<0.17"
)
optdepends=(
"alacritty: Terminal emulator used by the default config"

View file

@ -23,9 +23,9 @@ depends=(
"libxcb"
"libxkbcommon.so"
"pango"
"pcre"
"pcre2"
"ttf-font"
"wlroots<0.16"
"wlroots<0.17"
)
optdepends=(
"alacritty: Terminal emulator used by the default config"

View file

@ -2,7 +2,7 @@
### CHANGE THESE VARIABLES BEFORE RELEASE:
# Change to current Sway base version!
%global SwayBaseVersion 1.7
%global SwayBaseVersion 1.8
# Change to current SwayFX tag!
%global Tag 0.1.1
@ -23,8 +23,8 @@ BuildRequires: pkgconfig(gdk-pixbuf-2.0)
BuildRequires: pkgconfig(json-c) >= 0.13
BuildRequires: pkgconfig(libdrm)
BuildRequires: pkgconfig(libevdev)
BuildRequires: pkgconfig(libinput) >= 1.6.0
BuildRequires: pkgconfig(libpcre)
BuildRequires: pkgconfig(libinput) >= 1.21.0
BuildRequires: pkgconfig(libpcre2)
BuildRequires: pkgconfig(libsystemd) >= 239
BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(pango)
@ -35,7 +35,7 @@ BuildRequires: pkgconfig(wayland-cursor)
BuildRequires: pkgconfig(wayland-egl)
BuildRequires: pkgconfig(wayland-server) >= 1.20.0
BuildRequires: pkgconfig(wayland-protocols) >= 1.24
BuildRequires: (pkgconfig(wlroots) >= 0.15.0 with pkgconfig(wlroots) < 0.16)
BuildRequires: (pkgconfig(wlroots) >= 0.16.0 with pkgconfig(wlroots) < 0.17)
BuildRequires: pkgconfig(xcb)
BuildRequires: pkgconfig(xkbcommon)
# Dmenu is the default launcher in sway

View file

@ -1,52 +1,45 @@
#define _POSIX_C_SOURCE 200809
#include <assert.h>
#include <cairo.h>
#include <errno.h>
#include <fcntl.h>
#include <pango/pangocairo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include "config.h"
#include "pool-buffer.h"
#include "util.h"
static int create_pool_file(size_t size, char **name) {
static const char template[] = "sway-client-XXXXXX";
const char *path = getenv("XDG_RUNTIME_DIR");
if (path == NULL) {
fprintf(stderr, "XDG_RUNTIME_DIR is not set\n");
return -1;
}
static int anonymous_shm_open(void) {
int retries = 100;
size_t name_size = strlen(template) + 1 + strlen(path) + 1;
*name = malloc(name_size);
if (*name == NULL) {
fprintf(stderr, "allocation failed\n");
return -1;
}
snprintf(*name, name_size, "%s/%s", path, template);
int fd = mkstemp(*name);
if (fd < 0) {
return -1;
}
if (!sway_set_cloexec(fd, true)) {
close(fd);
return -1;
}
if (ftruncate(fd, size) < 0) {
close(fd);
return -1;
}
do {
// try a probably-unique name
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
pid_t pid = getpid();
char name[50];
snprintf(name, sizeof(name), "/sway-%x-%x",
(unsigned int)pid, (unsigned int)ts.tv_nsec);
// shm_open guarantees that O_CLOEXEC is set
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
shm_unlink(name);
return fd;
}
--retries;
} while (retries > 0 && errno == EEXIST);
return -1;
}
static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
struct pool_buffer *buffer = data;
buffer->busy = false;
@ -62,17 +55,20 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm,
uint32_t stride = width * 4;
size_t size = stride * height;
char *name;
int fd = create_pool_file(size, &name);
assert(fd != -1);
int fd = anonymous_shm_open();
if (fd == -1) {
return NULL;
}
if (ftruncate(fd, size) < 0) {
close(fd);
return NULL;
}
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
buf->buffer = wl_shm_pool_create_buffer(pool, 0,
width, height, stride, format);
wl_shm_pool_destroy(pool);
close(fd);
unlink(name);
free(name);
buf->size = size;
buf->width = width;

350
common/gesture.c Normal file
View file

@ -0,0 +1,350 @@
#define _POSIX_C_SOURCE 200809L
#include "gesture.h"
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
#include "log.h"
#include "stringop.h"
const uint8_t GESTURE_FINGERS_ANY = 0;
// Helper to easily allocate and format string
static char *strformat(const char *format, ...) {
va_list args;
va_start(args, format);
int length = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
char *result = malloc(length);
if (result) {
va_start(args, format);
vsnprintf(result, length, format, args);
va_end(args);
}
return result;
}
char *gesture_parse(const char *input, struct gesture *output) {
// Clear output in case of failure
output->type = GESTURE_TYPE_NONE;
output->fingers = GESTURE_FINGERS_ANY;
output->directions = GESTURE_DIRECTION_NONE;
// Split input type, fingers and directions
list_t *split = split_string(input, ":");
if (split->length < 1 || split->length > 3) {
return strformat(
"expected <gesture>[:<fingers>][:direction], got %s",
input);
}
// Parse gesture type
if (strcmp(split->items[0], "hold") == 0) {
output->type = GESTURE_TYPE_HOLD;
} else if (strcmp(split->items[0], "pinch") == 0) {
output->type = GESTURE_TYPE_PINCH;
} else if (strcmp(split->items[0], "swipe") == 0) {
output->type = GESTURE_TYPE_SWIPE;
} else {
return strformat("expected hold|pinch|swipe, got %s",
split->items[0]);
}
// Parse optional arguments
if (split->length > 1) {
char *next = split->items[1];
// Try to parse as finger count (1-9)
if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {
output->fingers = atoi(next);
// Move to next if available
next = split->length == 3 ? split->items[2] : NULL;
} else if (split->length == 3) {
// Fail here if argument can only be finger count
return strformat("expected 1-9, got %s", next);
}
// If there is an argument left, try to parse as direction
if (next) {
list_t *directions = split_string(next, "+");
for (int i = 0; i < directions->length; ++i) {
const char *item = directions->items[i];
if (strcmp(item, "any") == 0) {
continue;
} else if (strcmp(item, "up") == 0) {
output->directions |= GESTURE_DIRECTION_UP;
} else if (strcmp(item, "down") == 0) {
output->directions |= GESTURE_DIRECTION_DOWN;
} else if (strcmp(item, "left") == 0) {
output->directions |= GESTURE_DIRECTION_LEFT;
} else if (strcmp(item, "right") == 0) {
output->directions |= GESTURE_DIRECTION_RIGHT;
} else if (strcmp(item, "inward") == 0) {
output->directions |= GESTURE_DIRECTION_INWARD;
} else if (strcmp(item, "outward") == 0) {
output->directions |= GESTURE_DIRECTION_OUTWARD;
} else if (strcmp(item, "clockwise") == 0) {
output->directions |= GESTURE_DIRECTION_CLOCKWISE;
} else if (strcmp(item, "counterclockwise") == 0) {
output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
} else {
return strformat("expected direction, got %s", item);
}
}
list_free_items_and_destroy(directions);
}
} // if optional args
list_free_items_and_destroy(split);
return NULL;
}
const char *gesture_type_string(enum gesture_type type) {
switch (type) {
case GESTURE_TYPE_NONE:
return "none";
case GESTURE_TYPE_HOLD:
return "hold";
case GESTURE_TYPE_PINCH:
return "pinch";
case GESTURE_TYPE_SWIPE:
return "swipe";
}
return NULL;
}
const char *gesture_direction_string(enum gesture_direction direction) {
switch (direction) {
case GESTURE_DIRECTION_NONE:
return "none";
case GESTURE_DIRECTION_UP:
return "up";
case GESTURE_DIRECTION_DOWN:
return "down";
case GESTURE_DIRECTION_LEFT:
return "left";
case GESTURE_DIRECTION_RIGHT:
return "right";
case GESTURE_DIRECTION_INWARD:
return "inward";
case GESTURE_DIRECTION_OUTWARD:
return "outward";
case GESTURE_DIRECTION_CLOCKWISE:
return "clockwise";
case GESTURE_DIRECTION_COUNTERCLOCKWISE:
return "counterclockwise";
}
return NULL;
}
// Helper to turn combination of directions flags into string representation.
static char *gesture_directions_to_string(uint32_t directions) {
char *result = NULL;
for (uint8_t bit = 0; bit < 32; bit++) {
uint32_t masked = directions & (1 << bit);
if (masked) {
const char *name = gesture_direction_string(masked);
if (!name) {
name = "unknown";
}
if (!result) {
result = strdup(name);
} else {
char *new = strformat("%s+%s", result, name);
free(result);
result = new;
}
}
}
if(!result) {
return strdup("any");
}
return result;
}
char *gesture_to_string(struct gesture *gesture) {
char *directions = gesture_directions_to_string(gesture->directions);
char *result = strformat("%s:%u:%s",
gesture_type_string(gesture->type),
gesture->fingers, directions);
free(directions);
return result;
}
bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) {
// Check that gesture type matches
if (target->type != type) {
return false;
}
// Check that finger count matches
if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) {
return false;
}
return true;
}
bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) {
// Check type and fingers
if (!gesture_check(target, to_match->type, to_match->fingers)) {
return false;
}
// Enforce exact matches ...
if (exact && target->directions != to_match->directions) {
return false;
}
// ... or ensure all target directions are matched
return (target->directions & to_match->directions) == target->directions;
}
bool gesture_equal(struct gesture *a, struct gesture *b) {
return a->type == b->type
&& a->fingers == b->fingers
&& a->directions == b->directions;
}
// Return count of set bits in directions bit field.
static uint8_t gesture_directions_count(uint32_t directions) {
uint8_t count = 0;
for (; directions; directions >>= 1) {
count += directions & 1;
}
return count;
}
// Compare direction bit count of two direction.
static int8_t gesture_directions_compare(uint32_t a, uint32_t b) {
return gesture_directions_count(a) - gesture_directions_count(b);
}
// Compare two direction based on their direction bit count
int8_t gesture_compare(struct gesture *a, struct gesture *b) {
return gesture_directions_compare(a->directions, b->directions);
}
void gesture_tracker_begin(struct gesture_tracker *tracker,
enum gesture_type type, uint8_t fingers) {
tracker->type = type;
tracker->fingers = fingers;
tracker->dx = 0.0;
tracker->dy = 0.0;
tracker->scale = 1.0;
tracker->rotation = 0.0;
sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture",
gesture_type_string(type), fingers);
}
bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) {
return tracker->type == type;
}
void gesture_tracker_update(struct gesture_tracker *tracker,
double dx, double dy, double scale, double rotation) {
if (tracker->type == GESTURE_TYPE_HOLD) {
sway_assert(false, "hold does not update.");
return;
}
tracker->dx += dx;
tracker->dy += dy;
if (tracker->type == GESTURE_TYPE_PINCH) {
tracker->scale = scale;
tracker->rotation += rotation;
}
sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f",
gesture_type_string(tracker->type),
tracker->fingers,
tracker->dx, tracker->dy,
tracker->scale, tracker->rotation);
}
void gesture_tracker_cancel(struct gesture_tracker *tracker) {
sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture",
gesture_type_string(tracker->type), tracker->fingers);
tracker->type = GESTURE_TYPE_NONE;
}
struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
struct gesture *result = calloc(1, sizeof(struct gesture));
// Ignore gesture under some threshold
// TODO: Make configurable
const double min_rotation = 5;
const double min_scale_delta = 0.1;
// Determine direction
switch(tracker->type) {
// Gestures with scale and rotation
case GESTURE_TYPE_PINCH:
if (tracker->rotation > min_rotation) {
result->directions |= GESTURE_DIRECTION_CLOCKWISE;
}
if (tracker->rotation < -min_rotation) {
result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
}
if (tracker->scale > (1.0 + min_scale_delta)) {
result->directions |= GESTURE_DIRECTION_OUTWARD;
}
if (tracker->scale < (1.0 - min_scale_delta)) {
result->directions |= GESTURE_DIRECTION_INWARD;
}
__attribute__ ((fallthrough));
// Gestures with dx and dy
case GESTURE_TYPE_SWIPE:
if (fabs(tracker->dx) > fabs(tracker->dy)) {
if (tracker->dx > 0) {
result->directions |= GESTURE_DIRECTION_RIGHT;
} else {
result->directions |= GESTURE_DIRECTION_LEFT;
}
} else {
if (tracker->dy > 0) {
result->directions |= GESTURE_DIRECTION_DOWN;
} else {
result->directions |= GESTURE_DIRECTION_UP;
}
}
// Gesture without any direction
case GESTURE_TYPE_HOLD:
break;
// Not tracking any gesture
case GESTURE_TYPE_NONE:
sway_assert(false, "Not tracking any gesture.");
return result;
}
result->type = tracker->type;
result->fingers = tracker->fingers;
char *description = gesture_to_string(result);
sway_log(SWAY_DEBUG, "end tracking gesture: %s", description);
free(description);
tracker->type = GESTURE_TYPE_NONE;
return result;
}

View file

@ -3,6 +3,7 @@ lib_sway_common = static_library(
files(
'background-image.c',
'cairo.c',
'gesture.c',
'ipc-client.c',
'log.c',
'loop.c',

View file

@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) {
return length;
}
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,
const char *text, double scale, bool markup) {
PangoLayout *layout = pango_cairo_create_layout(cairo);
PangoAttrList *attrs;
@ -73,16 +73,14 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
}
pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
PangoFontDescription *desc = pango_font_description_from_string(font);
pango_layout_set_font_description(layout, desc);
pango_layout_set_single_paragraph_mode(layout, 1);
pango_layout_set_attributes(layout, attrs);
pango_attr_list_unref(attrs);
pango_font_description_free(desc);
return layout;
}
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
int *baseline, double scale, bool markup, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
@ -99,7 +97,7 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
vsnprintf(buf, length, fmt, args);
va_end(args);
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
pango_cairo_update_layout(cairo, layout);
pango_layout_get_pixel_size(layout, width, height);
if (baseline) {
@ -109,10 +107,9 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
free(buf);
}
void get_text_metrics(const char *font, int *height, int *baseline) {
void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) {
cairo_t *cairo = cairo_create(NULL);
PangoContext *pango = pango_cairo_create_context(cairo);
PangoFontDescription *description = pango_font_description_from_string(font);
// When passing NULL as a language, pango uses the current locale.
PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);
@ -120,12 +117,11 @@ void get_text_metrics(const char *font, int *height, int *baseline) {
*height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
pango_font_metrics_unref(metrics);
pango_font_description_free(description);
g_object_unref(pango);
cairo_destroy(cairo);
}
void render_text(cairo_t *cairo, const char *font,
void render_text(cairo_t *cairo, const PangoFontDescription *desc,
double scale, bool markup, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
@ -142,7 +138,7 @@ void render_text(cairo_t *cairo, const char *font,
vsnprintf(buf, length, fmt, args);
va_end(args);
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
cairo_font_options_t *fo = cairo_font_options_create();
cairo_get_font_options(cairo, fo);
pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);

View file

@ -2,7 +2,7 @@
_sway()
{
local cur prev
local cur prev short long
_get_comp_words_by_ref cur prev
short=(

View file

@ -2,7 +2,7 @@
_swaybar()
{
local cur prev
local cur prev short long
_get_comp_words_by_ref cur prev
short=(

View file

@ -2,7 +2,7 @@
_swaymsg()
{
local cur prev
local cur prev types short long
_get_comp_words_by_ref cur prev
types=(

View file

@ -46,7 +46,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
#
# exec swayidle -w \
# timeout 300 'swaylock -f -c 000000' \
# timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \
# timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
# before-sleep 'swaylock -f -c 000000'
#
# This will lock your screen after 300 seconds of inactivity, then turn off

View file

@ -13,18 +13,32 @@
## See `man 1 grimshot` or `grimshot usage` for further details.
getTargetDirectory() {
test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && \
. ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" && \
. "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
echo ${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}
echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}"
}
if [ "$1" = "--notify" ]; then
NOTIFY=yes
shift 1
else
NOTIFY=no
fi
CURSOR=
while [ $# -gt 0 ]; do
key="$1"
case $key in
-n|--notify)
NOTIFY=yes
shift # past argument
;;
-c|--cursor)
CURSOR=yes
shift # past argument
;;
*) # unknown option
break # done with parsing --flags
;;
esac
done
ACTION=${1:-usage}
SUBJECT=${2:-screen}
@ -32,7 +46,7 @@ FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then
echo "Usage:"
echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE|-]"
echo " grimshot [--notify] [--cursor] (copy|save) [active|screen|output|area|window] [FILE|-]"
echo " grimshot check"
echo " grimshot usage"
echo ""
@ -67,7 +81,7 @@ notifyError() {
MESSAGE=${1:-"Error taking screenshot with grim"}
notify -u critical "$TITLE" "$MESSAGE"
else
echo $1
echo "$1"
fi
}
@ -91,12 +105,12 @@ takeScreenshot() {
FILE=$1
GEOM=$2
OUTPUT=$3
if [ ! -z "$OUTPUT" ]; then
grim -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
if [ -n "$OUTPUT" ]; then
grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
elif [ -z "$GEOM" ]; then
grim "$FILE" || die "Unable to invoke grim"
grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim"
else
grim -g "$GEOM" "$FILE" || die "Unable to invoke grim"
grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim"
fi
}
@ -147,7 +161,7 @@ else
TITLE="Screenshot of $SUBJECT"
MESSAGE=$(basename "$FILE")
notifyOk "$MESSAGE" "$TITLE"
echo $FILE
echo "$FILE"
else
notifyError "Error taking screenshot with grim"
fi

View file

@ -1,11 +1,11 @@
.\" Generated by scdoc 1.11.1
.\" Generated by scdoc 1.11.2
.\" Complete documentation for this program is not available as a GNU info page
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.nh
.ad l
.\" Begin generated content:
.TH "grimshot" "1" "2021-02-23"
.TH "grimshot" "1" "2022-03-31"
.P
.SH NAME
.P
@ -13,7 +13,7 @@ grimshot - a helper for screenshots within sway
.P
.SH SYNOPSIS
.P
\fBgrimshot\fR [--notify] (copy|save) [TARGET] [FILE]
\fBgrimshot\fR [--notify] [--cursor] (copy|save) [TARGET] [FILE]
.br
\fBgrimshot\fR check
.br
@ -26,12 +26,17 @@ grimshot - a helper for screenshots within sway
Show notifications to the user that a screenshot has been taken.\&
.P
.RE
\fB--cursor\fR
.RS 4
Include cursors in the screenshot.\&
.P
.RE
\fBsave\fR
.RS 4
Save the screenshot into a regular file.\& Grimshot will write images
files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined
in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\&
Set FILE to '-' to pipe the output to STDOUT.\&
Set FILE to '\&-'\& to pipe the output to STDOUT.\&
.P
.RE
\fBcopy\fR

View file

@ -6,7 +6,7 @@ grimshot - a helper for screenshots within sway
# SYNOPSIS
*grimshot* [--notify] (copy|save) [TARGET] [FILE]++
*grimshot* [--notify] [--cursor] (copy|save) [TARGET] [FILE]++
*grimshot* check++
*grimshot* usage
@ -15,8 +15,11 @@ grimshot - a helper for screenshots within sway
*--notify*
Show notifications to the user that a screenshot has been taken.
*--cursor*
Include cursors in the screenshot.
*save*
Save the screenshot into a regular file. Grimshot will write images
Save the screenshot into a regular file. Grimshot will write image
files to *XDG_SCREENSHOTS_DIR* if this is set (or defined
in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*.
Set FILE to '-' to pipe the output to STDOUT.

104
include/gesture.h Normal file
View file

@ -0,0 +1,104 @@
#ifndef _SWAY_GESTURE_H
#define _SWAY_GESTURE_H
#include <stdbool.h>
#include <stdint.h>
/**
* A gesture type used in binding.
*/
enum gesture_type {
GESTURE_TYPE_NONE = 0,
GESTURE_TYPE_HOLD,
GESTURE_TYPE_PINCH,
GESTURE_TYPE_SWIPE,
};
// Turns single type enum value to constant string representation.
const char *gesture_type_string(enum gesture_type direction);
// Value to use to accept any finger count
extern const uint8_t GESTURE_FINGERS_ANY;
/**
* A gesture direction used in binding.
*/
enum gesture_direction {
GESTURE_DIRECTION_NONE = 0,
// Directions based on delta x and y
GESTURE_DIRECTION_UP = 1 << 0,
GESTURE_DIRECTION_DOWN = 1 << 1,
GESTURE_DIRECTION_LEFT = 1 << 2,
GESTURE_DIRECTION_RIGHT = 1 << 3,
// Directions based on scale
GESTURE_DIRECTION_INWARD = 1 << 4,
GESTURE_DIRECTION_OUTWARD = 1 << 5,
// Directions based on rotation
GESTURE_DIRECTION_CLOCKWISE = 1 << 6,
GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7,
};
// Turns single direction enum value to constant string representation.
const char *gesture_direction_string(enum gesture_direction direction);
/**
* Struct representing a pointer gesture
*/
struct gesture {
enum gesture_type type;
uint8_t fingers;
uint32_t directions;
};
/**
* Parses gesture from <gesture>[:<fingers>][:<directions>] string.
*
* Return NULL on success, otherwise error message string
*/
char *gesture_parse(const char *input, struct gesture *output);
// Turns gesture into string representation
char *gesture_to_string(struct gesture *gesture);
// Check if gesture is of certain type and finger count.
bool gesture_check(struct gesture *target,
enum gesture_type type, uint8_t fingers);
// Check if a gesture target/binding is match by other gesture/input
bool gesture_match(struct gesture *target,
struct gesture *to_match, bool exact);
// Returns true if gesture are exactly the same
bool gesture_equal(struct gesture *a, struct gesture *b);
// Compare distance between two matched target gestures.
int8_t gesture_compare(struct gesture *a, struct gesture *b);
// Small helper struct to track gestures over time
struct gesture_tracker {
enum gesture_type type;
uint8_t fingers;
double dx, dy;
double scale;
double rotation;
};
// Begin gesture tracking
void gesture_tracker_begin(struct gesture_tracker *tracker,
enum gesture_type type, uint8_t fingers);
// Check if the provides type is currently being tracked
bool gesture_tracker_check(struct gesture_tracker *tracker,
enum gesture_type type);
// Update gesture track with new data point
void gesture_tracker_update(struct gesture_tracker *tracker, double dx,
double dy, double scale, double rotation);
// Reset tracker
void gesture_tracker_cancel(struct gesture_tracker *tracker);
// Reset tracker and return gesture tracked
struct gesture *gesture_tracker_end(struct gesture_tracker *tracker);
#endif

View file

@ -13,12 +13,12 @@
* escaped string to dest if provided.
*/
size_t escape_markup_text(const char *src, char *dest);
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,
const char *text, double scale, bool markup);
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
int *baseline, double scale, bool markup, const char *fmt, ...);
void get_text_metrics(const char *font, int *height, int *baseline);
void render_text(cairo_t *cairo, const char *font,
void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline);
void render_text(cairo_t *cairo, PangoFontDescription *desc,
double scale, bool markup, const char *fmt, ...);
#endif

View file

@ -2,6 +2,7 @@
#define _SWAY_STRINGOP_H
#include <stdbool.h>
#include <stddef.h>
#include "list.h"
void strip_whitespace(char *str);

View file

@ -106,6 +106,7 @@ sway_cmd cmd_exec_process;
sway_cmd cmd_assign;
sway_cmd cmd_bar;
sway_cmd cmd_bindcode;
sway_cmd cmd_bindgesture;
sway_cmd cmd_bindswitch;
sway_cmd cmd_bindsym;
sway_cmd cmd_border;
@ -196,6 +197,7 @@ sway_cmd cmd_titlebar_border_thickness;
sway_cmd cmd_titlebar_padding;
sway_cmd cmd_unbindcode;
sway_cmd cmd_unbindswitch;
sway_cmd cmd_unbindgesture;
sway_cmd cmd_unbindsym;
sway_cmd cmd_unmark;
sway_cmd cmd_urgent;
@ -255,6 +257,7 @@ sway_cmd input_cmd_click_method;
sway_cmd input_cmd_drag;
sway_cmd input_cmd_drag_lock;
sway_cmd input_cmd_dwt;
sway_cmd input_cmd_dwtp;
sway_cmd input_cmd_events;
sway_cmd input_cmd_left_handed;
sway_cmd input_cmd_map_from_region;
@ -290,12 +293,14 @@ sway_cmd output_cmd_max_render_time;
sway_cmd output_cmd_mode;
sway_cmd output_cmd_modeline;
sway_cmd output_cmd_position;
sway_cmd output_cmd_power;
sway_cmd output_cmd_render_bit_depth;
sway_cmd output_cmd_scale;
sway_cmd output_cmd_scale_filter;
sway_cmd output_cmd_subpixel;
sway_cmd output_cmd_toggle;
sway_cmd output_cmd_transform;
sway_cmd output_cmd_unplug;
sway_cmd seat_cmd_attach;
sway_cmd seat_cmd_cursor;

View file

@ -10,12 +10,14 @@
#include <xkbcommon/xkbcommon.h>
#include <xf86drmMode.h>
#include "../include/config.h"
#include "gesture.h"
#include "list.h"
#include "swaynag.h"
#include "tree/container.h"
#include "sway/input/tablet.h"
#include "sway/tree/root.h"
#include "wlr-layer-shell-unstable-v1-protocol.h"
#include <pango/pangocairo.h>
// TODO: Refactor this shit
@ -32,7 +34,8 @@ enum binding_input_type {
BINDING_KEYSYM,
BINDING_MOUSECODE,
BINDING_MOUSESYM,
BINDING_SWITCH
BINDING_SWITCH, // dummy, only used to call seat_execute_command
BINDING_GESTURE // dummy, only used to call seat_execute_command
};
enum binding_flags {
@ -45,10 +48,11 @@ enum binding_flags {
BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload
BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key
BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match
};
/**
* A key binding and an associated command.
* A key (or mouse) binding and an associated command.
*/
struct sway_binding {
enum binding_input_type type;
@ -62,12 +66,10 @@ struct sway_binding {
char *command;
};
/**
* A mouse binding and an associated command.
*/
struct sway_mouse_binding {
uint32_t button;
char *command;
enum sway_switch_trigger {
SWAY_SWITCH_TRIGGER_OFF,
SWAY_SWITCH_TRIGGER_ON,
SWAY_SWITCH_TRIGGER_TOGGLE,
};
/**
@ -75,11 +77,21 @@ struct sway_mouse_binding {
*/
struct sway_switch_binding {
enum wlr_switch_type type;
enum wlr_switch_state state;
enum sway_switch_trigger trigger;
uint32_t flags;
char *command;
};
/**
* A gesture binding and an associated command.
*/
struct sway_gesture_binding {
char *input;
uint32_t flags;
struct gesture gesture;
char *command;
};
/**
* Focus on window activation.
*/
@ -99,6 +111,7 @@ struct sway_mode {
list_t *keycode_bindings;
list_t *mouse_bindings;
list_t *switch_bindings;
list_t *gesture_bindings;
bool pango;
};
@ -137,6 +150,7 @@ struct input_config {
int drag;
int drag_lock;
int dwt;
int dwtp;
int left_handed;
int middle_emulation;
int natural_scroll;
@ -234,12 +248,6 @@ struct seat_config {
} xcursor_theme;
};
enum config_dpms {
DPMS_IGNORE,
DPMS_ON,
DPMS_OFF,
};
enum scale_filter_mode {
SCALE_FILTER_DEFAULT, // the default is currently smart
SCALE_FILTER_LINEAR,
@ -261,6 +269,7 @@ enum render_bit_depth {
struct output_config {
char *name;
int enabled;
int power;
int width, height;
float refresh_rate;
int custom_mode;
@ -277,7 +286,6 @@ struct output_config {
char *background;
char *background_option;
char *background_fallback;
enum config_dpms dpms_state;
};
/**
@ -501,7 +509,8 @@ struct sway_config {
char *floating_scroll_right_cmd;
enum sway_container_layout default_orientation;
enum sway_container_layout default_layout;
char *font;
char *font; // Used for IPC.
PangoFontDescription *font_description; // Used internally for rendering and validating.
int font_height;
int font_baseline;
bool pango_markup;
@ -700,6 +709,8 @@ void free_sway_binding(struct sway_binding *sb);
void free_switch_binding(struct sway_switch_binding *binding);
void free_gesture_binding(struct sway_gesture_binding *binding);
void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
void load_swaybar(struct bar_config *bar);

View file

@ -1,7 +1,8 @@
#ifndef _SWAY_CRITERIA_H
#define _SWAY_CRITERIA_H
#include <pcre.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "config.h"
#include "list.h"
#include "tree/view.h"
@ -15,13 +16,13 @@ enum criteria_type {
};
enum pattern_type {
PATTERN_PCRE,
PATTERN_PCRE2,
PATTERN_FOCUSED,
};
struct pattern {
enum pattern_type match_type;
pcre *regex;
pcre2_code *regex;
};
struct criteria {

View file

@ -1,4 +1,4 @@
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
struct sway_container;
struct sway_view;

View file

@ -0,0 +1,32 @@
#ifndef _SWAY_LAUNCHER_H
#define _SWAY_LAUNCHER_H
#include <stdlib.h>
struct launcher_ctx {
pid_t pid;
char *name;
struct wlr_xdg_activation_token_v1 *token;
struct wl_listener token_destroy;
bool activated;
struct sway_node *node;
struct wl_listener node_destroy;
struct wl_list link; // sway_server::pending_launcher_ctxs
};
struct launcher_ctx *launcher_ctx_find_pid(pid_t pid);
struct sway_workspace *launcher_ctx_get_workspace(struct launcher_ctx *ctx);
void launcher_ctx_consume(struct launcher_ctx *ctx);
void launcher_ctx_destroy(struct launcher_ctx *ctx);
struct launcher_ctx *launcher_ctx_create(void);
const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx);
#endif

View file

@ -4,7 +4,7 @@
#include <stdint.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_pointer_gestures_v1.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include "sway/input/seat.h"
#include "config.h"
@ -36,6 +36,8 @@ struct sway_cursor {
bool active_confine_requires_warp;
struct wlr_pointer_gestures_v1 *pointer_gestures;
struct wl_listener hold_begin;
struct wl_listener hold_end;
struct wl_listener pinch_begin;
struct wl_listener pinch_update;
struct wl_listener pinch_end;
@ -110,7 +112,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
enum wlr_button_state state);
void dispatch_cursor_axis(struct sway_cursor *cursor,
struct wlr_event_pointer_axis *event);
struct wlr_pointer_axis_event *event);
void cursor_set_image(struct sway_cursor *cursor, const char *image,
struct wl_client *client);

View file

@ -50,6 +50,7 @@ struct sway_shortcut_state {
struct sway_keyboard {
struct sway_seat_device *seat_device;
struct wlr_keyboard *wlr;
struct xkb_keymap *keymap;
xkb_layout_index_t effective_layout;

View file

@ -2,7 +2,7 @@
#define _SWAY_INPUT_LIBINPUT_H
#include "sway/input/input-manager.h"
void sway_input_configure_libinput_device(struct sway_input_device *device);
bool sway_input_configure_libinput_device(struct sway_input_device *device);
void sway_input_reset_libinput_device(struct sway_input_device *device);

View file

@ -18,7 +18,23 @@ struct sway_seatop_impl {
enum wlr_button_state state);
void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec);
void (*pointer_axis)(struct sway_seat *seat,
struct wlr_event_pointer_axis *event);
struct wlr_pointer_axis_event *event);
void (*hold_begin)(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event);
void (*hold_end)(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event);
void (*pinch_begin)(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event);
void (*pinch_update)(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event);
void (*pinch_end)(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event);
void (*swipe_begin)(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event);
void (*swipe_update)(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event);
void (*swipe_end)(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event);
void (*rebase)(struct sway_seat *seat, uint32_t time_msec);
void (*tablet_tool_motion)(struct sway_seat *seat,
struct sway_tablet_tool *tool, uint32_t time_msec);
@ -274,7 +290,7 @@ void seatop_button(struct sway_seat *seat, uint32_t time_msec,
void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);
void seatop_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event);
struct wlr_pointer_axis_event *event);
void seatop_tablet_tool_tip(struct sway_seat *seat,
struct sway_tablet_tool *tool, uint32_t time_msec,
@ -283,6 +299,25 @@ void seatop_tablet_tool_tip(struct sway_seat *seat,
void seatop_tablet_tool_motion(struct sway_seat *seat,
struct sway_tablet_tool *tool, uint32_t time_msec);
void seatop_hold_begin(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event);
void seatop_hold_end(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event);
void seatop_pinch_begin(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event);
void seatop_pinch_update(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event);
void seatop_pinch_end(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event);
void seatop_swipe_begin(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event);
void seatop_swipe_update(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event);
void seatop_swipe_end(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event);
void seatop_rebase(struct sway_seat *seat, uint32_t time_msec);
/**

View file

@ -5,6 +5,7 @@
struct sway_switch {
struct sway_seat_device *seat_device;
struct wlr_switch *wlr;
enum wlr_switch_state state;
enum wlr_switch_type type;

View file

@ -32,6 +32,7 @@ struct sway_tablet_pad {
struct wl_list link;
struct sway_seat_device *seat_device;
struct sway_tablet *tablet;
struct wlr_tablet_pad *wlr;
struct wlr_tablet_v2_tablet_pad *tablet_v2_pad;
struct wl_listener attach;

View file

@ -3,7 +3,7 @@
#include <wlr/types/wlr_text_input_v3.h>
#include <wlr/types/wlr_input_method_v2.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include "sway/input/seat.h"
/**

View file

@ -1,6 +1,7 @@
#ifndef _SWAY_IPC_JSON_H
#define _SWAY_IPC_JSON_H
#include <json.h>
#include "sway/output.h"
#include "sway/tree/container.h"
#include "sway/input/input-manager.h"
@ -9,6 +10,7 @@ json_object *ipc_json_get_version(void);
json_object *ipc_json_get_binding_mode(void);
json_object *ipc_json_describe_disabled_output(struct sway_output *o);
json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *o);
json_object *ipc_json_describe_node(struct sway_node *node);
json_object *ipc_json_describe_node_recursive(struct sway_node *node);
json_object *ipc_json_describe_input(struct sway_input_device *device);

View file

@ -1,7 +1,7 @@
#ifndef _SWAY_LAYERS_H
#define _SWAY_LAYERS_H
#include <stdbool.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_layer_shell_v1.h>
enum layer_parent {

View file

@ -3,6 +3,7 @@
#include <time.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_output.h>
#include "config.h"
#include "sway/desktop/fx_renderer.h"
@ -27,13 +28,13 @@ struct sway_output {
struct wlr_box usable_area;
struct timespec last_frame;
struct wlr_output_damage *damage;
struct wlr_damage_ring damage_ring;
int lx, ly; // layout coords
int width, height; // transformed buffer size
enum wl_output_subpixel detected_subpixel;
enum scale_filter_mode scale_filter;
// last applied mode when the output is DPMS'ed
// last applied mode when the output is powered off
struct wlr_output_mode *current_mode;
bool enabling, enabled;
@ -45,8 +46,9 @@ struct sway_output {
struct wl_listener commit;
struct wl_listener mode;
struct wl_listener present;
struct wl_listener damage_destroy;
struct wl_listener damage_frame;
struct wl_listener damage;
struct wl_listener frame;
struct wl_listener needs_frame;
struct {
struct wl_signal disable;
@ -58,6 +60,12 @@ struct sway_output {
struct wl_event_source *repaint_timer;
};
struct sway_output_non_desktop {
struct wlr_output *wlr_output;
struct wl_listener destroy;
};
struct sway_output *output_create(struct wlr_output *wlr_output);
void output_destroy(struct sway_output *output);
@ -184,4 +192,6 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data);
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
#endif

View file

@ -16,6 +16,7 @@
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_text_input_v3.h>
#include <wlr/types/wlr_xdg_shell.h>
@ -52,6 +53,7 @@ struct sway_server {
struct wl_listener output_layout_change;
struct wlr_idle *idle;
struct wlr_idle_notifier_v1 *idle_notifier_v1;
struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1;
struct wlr_layer_shell_v1 *layer_shell;
@ -90,6 +92,20 @@ struct sway_server {
struct wl_listener output_manager_apply;
struct wl_listener output_manager_test;
struct {
bool locked;
struct wlr_session_lock_manager_v1 *manager;
struct wlr_session_lock_v1 *lock;
struct wlr_surface *focused;
struct wl_listener lock_new_surface;
struct wl_listener lock_unlock;
struct wl_listener lock_destroy;
struct wl_listener new_lock;
struct wl_listener manager_destroy;
} session_lock;
struct wlr_output_power_manager_v1 *output_power_manager_v1;
struct wl_listener output_power_manager_set_mode;
struct wlr_input_method_manager_v2 *input_method;
@ -99,6 +115,8 @@ struct sway_server {
struct wlr_xdg_activation_v1 *xdg_activation_v1;
struct wl_listener xdg_activation_v1_request_activate;
struct wl_list pending_launcher_ctxs; // launcher_ctx::link
// The timeout for transactions, after which a transaction is applied
// regardless of readiness.
size_t txn_timeout_ms;
@ -135,8 +153,6 @@ struct sway_debug {
extern struct sway_debug debug;
/* Prepares an unprivileged server_init by performing all privileged operations in advance */
bool server_privileged_prepare(struct sway_server *server);
bool server_init(struct sway_server *server);
void server_fini(struct sway_server *server);
bool server_start(struct sway_server *server);
@ -149,6 +165,7 @@ void handle_new_output(struct wl_listener *listener, void *data);
void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
void handle_layer_shell_surface(struct wl_listener *listener, void *data);
void sway_session_lock_init(void);
void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
#if HAVE_XWAYLAND
void handle_xwayland_surface(struct wl_listener *listener, void *data);
@ -159,4 +176,6 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data);
void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
void *data);
void set_rr_scheduling(void);
#endif

View file

@ -1,6 +1,6 @@
#ifndef _SWAY_SURFACE_H
#define _SWAY_SURFACE_H
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
struct sway_surface {
struct wlr_surface *wlr_surface;

View file

@ -2,7 +2,7 @@
#define _SWAY_CONTAINER_H
#include <stdint.h>
#include <sys/types.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include "list.h"
#include "sway/tree/node.h"
@ -368,7 +368,7 @@ bool container_is_sticky_or_child(struct sway_container *con);
* This will destroy pairs of redundant H/V splits
* e.g. H[V[H[app app]] app] -> H[app app app]
* The middle "V[H[" are eliminated by a call to container_squash
* on the V[ con. It's grandchildren are added to it's parent.
* on the V[ con. It's grandchildren are added to its parent.
*
* This function is roughly equivalent to i3's tree_flatten here:
* https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651

View file

@ -28,6 +28,7 @@ struct sway_root {
double width, height;
list_t *outputs; // struct sway_output
list_t *non_desktop_outputs; // struct sway_output_non_desktop
list_t *scratchpad; // struct sway_container
// For when there's no connected outputs
@ -68,12 +69,6 @@ void root_scratchpad_show(struct sway_container *con);
*/
void root_scratchpad_hide(struct sway_container *con);
struct sway_workspace *root_workspace_for_pid(pid_t pid);
void root_record_workspace_pid(pid_t pid);
void root_remove_workspace_pid(pid_t pid);
void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
void *data);
@ -91,6 +86,4 @@ struct sway_container *root_find_container(
void root_get_box(struct sway_root *root, struct wlr_box *box);
void root_rename_pid_workspaces(const char *old_name, const char *new_name);
#endif

View file

@ -1,7 +1,7 @@
#ifndef _SWAY_VIEW_H
#define _SWAY_VIEW_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include "config.h"
#if HAVE_XWAYLAND
#include <wlr/xwayland.h>
@ -74,6 +74,7 @@ struct sway_view {
struct sway_xdg_decoration *xdg_decoration;
pid_t pid;
struct launcher_ctx *ctx;
// The size the view would want to be if it weren't tiled.
// Used when changing a view from tiled to floating.
@ -109,7 +110,7 @@ struct sway_view {
list_t *executed_criteria; // struct criteria *
union {
struct wlr_xdg_surface *wlr_xdg_surface;
struct wlr_xdg_toplevel *wlr_xdg_toplevel;
#if HAVE_XWAYLAND
struct wlr_xwayland_surface *wlr_xwayland_surface;
#endif
@ -132,6 +133,7 @@ struct sway_xdg_shell_view {
struct wl_listener commit;
struct wl_listener request_move;
struct wl_listener request_resize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen;
struct wl_listener set_title;
struct wl_listener set_app_id;
@ -155,6 +157,7 @@ struct sway_xwayland_view {
struct wl_listener set_title;
struct wl_listener set_class;
struct wl_listener set_role;
struct wl_listener set_startup_id;
struct wl_listener set_window_type;
struct wl_listener set_hints;
struct wl_listener set_decorations;
@ -170,6 +173,7 @@ struct sway_xwayland_unmanaged {
int lx, ly;
struct wl_listener request_activate;
struct wl_listener request_configure;
struct wl_listener request_fullscreen;
struct wl_listener commit;
@ -217,7 +221,7 @@ struct sway_subsurface {
struct sway_xdg_popup {
struct sway_view_child child;
struct wlr_xdg_surface *wlr_xdg_surface;
struct wlr_xdg_popup *wlr_xdg_popup;
struct wl_listener new_popup;
struct wl_listener destroy;
@ -371,4 +375,6 @@ void view_save_buffer(struct sway_view *view);
bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);
void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
#endif

View file

@ -58,7 +58,6 @@ struct swaybar_output {
struct zxdg_output_v1 *xdg_output;
struct wl_surface *surface;
struct zwlr_layer_surface_v1 *layer_surface;
struct wl_region *input_region;
uint32_t wl_name;
struct wl_list workspaces; // swaybar_workspace::link

View file

@ -6,6 +6,7 @@
#include "../include/config.h"
#include "list.h"
#include "util.h"
#include <pango/pangocairo.h>
struct box_colors {
uint32_t border;
@ -28,7 +29,7 @@ struct swaybar_config {
char *status_command;
bool pango_markup;
uint32_t position; // zwlr_layer_surface_v1_anchor
char *font;
PangoFontDescription *font_description;
char *sep_symbol;
char *mode;
char *hidden_state;

View file

@ -30,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block);
bool i3bar_handle_readable(struct status_line *status);
enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
struct i3bar_block *block, double x, double y, double rx, double ry,
double w, double h, int scale, uint32_t button);
double w, double h, int scale, uint32_t button, bool released);
#endif

View file

@ -49,7 +49,7 @@ struct swaybar_hotspot {
int x, y, width, height;
enum hotspot_event_handling (*callback)(struct swaybar_output *output,
struct swaybar_hotspot *hotspot, double x, double y, uint32_t button,
void *data);
bool released, void *data);
void (*destroy)(void *data);
void *data;
};

View file

@ -67,7 +67,7 @@ struct swaynag_details {
int offset;
int visible_lines;
int total_lines;
struct swaynag_button *button_details;
struct swaynag_button button_details;
struct swaynag_button button_up;
struct swaynag_button button_down;
};

View file

@ -4,7 +4,8 @@
struct swaynag_type {
char *name;
char *font;
char *font; // Used for debugging.
PangoFontDescription *font_description;
char *output;
uint32_t anchors;
int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset

View file

@ -35,14 +35,24 @@ if is_freebsd
add_project_arguments('-D_C11_SOURCE', language: 'c')
endif
# Execute the wlroots subproject, if any
wlroots_version = ['>=0.16.0', '<0.17.0']
subproject(
'wlroots',
default_options: ['examples=false'],
required: false,
version: wlroots_version,
)
jsonc = dependency('json-c', version: '>=0.13')
pcre = dependency('libpcre')
wayland_server = dependency('wayland-server', version: '>=1.20.0')
pcre2 = dependency('libpcre2-8')
wayland_server = dependency('wayland-server', version: '>=1.21.0')
wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
wayland_egl = dependency('wayland-egl')
egl = dependency('egl')
wayland_protos = dependency('wayland-protocols', version: '>=1.24')
wlroots = dependency('wlroots', version: wlroots_version)
xkbcommon = dependency('xkbcommon')
cairo = dependency('cairo')
pango = dependency('pango')
@ -51,7 +61,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
pixman = dependency('pixman-1')
glesv2 = dependency('glesv2')
libevdev = dependency('libevdev')
libinput = dependency('libinput', version: '>=1.6.0')
libinput = dependency('libinput', version: '>=1.21.0')
xcb = dependency('xcb', required: get_option('xwayland'))
drm_full = dependency('libdrm') # only needed for drm_fourcc.h
drm = drm_full.partial_dependency(compile_args: true, includes: true)
@ -60,20 +70,8 @@ bash_comp = dependency('bash-completion', required: false)
fish_comp = dependency('fish', required: false)
math = cc.find_library('m')
rt = cc.find_library('rt')
# Try first to find wlroots as a subproject, then as a system dependency
wlroots_version = ['>=0.15.0', '<0.16.0']
wlroots_proj = subproject(
'wlroots',
default_options: ['examples=false'],
required: false,
version: wlroots_version,
)
if wlroots_proj.found()
wlroots = wlroots_proj.get_variable('wlroots')
else
wlroots = dependency('wlroots', version: wlroots_version)
endif
xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland'))
threads = dependency('threads') # for pthread_setschedparam
wlroots_features = {
'xwayland': false,
@ -87,7 +85,7 @@ endforeach
if get_option('xwayland').enabled() and not wlroots_features['xwayland']
error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
endif
have_xwayland = xcb.found() and wlroots_features['xwayland']
have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland']
if get_option('sd-bus-provider') == 'auto'
if not get_option('tray').disabled()

View file

@ -1,81 +1,42 @@
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true)
if wayland_scanner_dep.found()
wayland_scanner_dep = dependency('wayland-scanner', native: true)
wayland_scanner = find_program(
wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'),
native: true,
)
else
wayland_scanner = find_program('wayland-scanner', native: true)
endif
protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'],
[wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['idle.xml'],
['wlr-input-inhibitor-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],
]
client_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-input-inhibitor-unstable-v1.xml'],
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml',
wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'idle.xml',
'wlr-input-inhibitor-unstable-v1.xml',
'wlr-output-power-management-unstable-v1.xml',
]
wl_protos_src = []
wl_protos_headers = []
foreach p : protocols
xml = join_paths(p)
foreach xml : protocols
wl_protos_src += custom_target(
xml.underscorify() + '_server_c',
xml.underscorify() + '_c',
input: xml,
output: '@BASENAME@-protocol.c',
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
)
wl_protos_headers += custom_target(
wl_protos_src += custom_target(
xml.underscorify() + '_server_h',
input: xml,
output: '@BASENAME@-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
)
endforeach
foreach p : client_protocols
xml = join_paths(p)
wl_protos_headers += custom_target(
wl_protos_src += custom_target(
xml.underscorify() + '_client_h',
input: xml,
output: '@BASENAME@-client-protocol.h',
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
)
endforeach
lib_client_protos = static_library(
'client_protos',
wl_protos_src + wl_protos_headers,
dependencies: wayland_client.partial_dependency(compile_args: true),
)
client_protos = declare_dependency(
link_with: lib_client_protos,
sources: wl_protos_headers,
)
lib_server_protos = static_library(
'server_protos',
wl_protos_src + wl_protos_headers,
dependencies: wayland_server.partial_dependency(compile_args: true),
)
server_protos = declare_dependency(
link_with: lib_server_protos,
sources: wl_protos_headers,
)

View file

@ -46,6 +46,7 @@ static const struct cmd_handler handlers[] = {
{ "assign", cmd_assign },
{ "bar", cmd_bar },
{ "bindcode", cmd_bindcode },
{ "bindgesture", cmd_bindgesture },
{ "bindswitch", cmd_bindswitch },
{ "bindsym", cmd_bindsym },
{ "client.background", cmd_client_noop },
@ -96,6 +97,7 @@ static const struct cmd_handler handlers[] = {
{ "titlebar_border_thickness", cmd_titlebar_border_thickness },
{ "titlebar_padding", cmd_titlebar_padding },
{ "unbindcode", cmd_unbindcode },
{ "unbindgesture", cmd_unbindgesture },
{ "unbindswitch", cmd_unbindswitch },
{ "unbindsym", cmd_unbindsym },
{ "workspace", cmd_workspace },
@ -412,6 +414,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
&& handler->handle != cmd_bindsym
&& handler->handle != cmd_bindcode
&& handler->handle != cmd_bindswitch
&& handler->handle != cmd_bindgesture
&& handler->handle != cmd_set
&& handler->handle != cmd_for_window
&& (*argv[i] == '\"' || *argv[i] == '\'')) {
@ -487,13 +490,19 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
}
results->status = status;
if (format) {
char *error = malloc(256);
char *error = NULL;
va_list args;
va_start(args, format);
if (error) {
vsnprintf(error, 256, format, args);
}
int slen = vsnprintf(NULL, 0, format, args);
va_end(args);
if (slen > 0) {
error = malloc(slen + 1);
if (error != NULL) {
va_start(args, format);
vsnprintf(error, slen + 1, format, args);
va_end(args);
}
}
results->error = error;
} else {
results->error = NULL;

View file

@ -47,7 +47,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a,
if (binding_a->type != binding_b->type) {
return false;
}
if (binding_a->state != binding_b->state) {
if (binding_a->trigger != binding_b->trigger) {
return false;
}
if ((binding_a->flags & BINDING_LOCKED) !=
@ -372,6 +372,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
strlen("--input-device=")) == 0) {
free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device="));
strip_quotes(binding->input);
} else if (strcmp("--no-warn", argv[0]) == 0) {
warn = false;
} else if (strcmp("--no-repeat", argv[0]) == 0) {
@ -551,11 +552,11 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
"unknown switch %s)", bindtype, split->items[0]);
}
if (strcmp(split->items[1], "on") == 0) {
binding->state = WLR_SWITCH_STATE_ON;
binding->trigger = SWAY_SWITCH_TRIGGER_ON;
} else if (strcmp(split->items[1], "off") == 0) {
binding->state = WLR_SWITCH_STATE_OFF;
binding->trigger = SWAY_SWITCH_TRIGGER_OFF;
} else if (strcmp(split->items[1], "toggle") == 0) {
binding->state = WLR_SWITCH_STATE_TOGGLE;
binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;
} else {
free_switch_binding(binding);
return cmd_results_new(CMD_FAILURE,

View file

@ -8,6 +8,8 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/server.h"
#include "sway/desktop/launcher.h"
#include "sway/server.h"
#include "sway/tree/container.h"
#include "sway/tree/root.h"
#include "sway/tree/workspace.h"
@ -25,11 +27,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
return error;
}
static void export_xdga_token(struct launcher_ctx *ctx) {
const char *token = launcher_ctx_get_token_name(ctx);
setenv("XDG_ACTIVATION_TOKEN", token, 1);
}
static void export_startup_id(struct launcher_ctx *ctx) {
const char *token = launcher_ctx_get_token_name(ctx);
setenv("DESKTOP_STARTUP_ID", token, 1);
}
struct cmd_results *cmd_exec_process(int argc, char **argv) {
struct cmd_results *error = NULL;
char *cmd = NULL;
bool no_startup_id = false;
if (strcmp(argv[0], "--no-startup-id") == 0) {
sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored.");
no_startup_id = true;
--argc; ++argv;
if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
return error;
@ -51,6 +64,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
}
pid_t pid, child;
struct launcher_ctx *ctx = launcher_ctx_create();
// Fork process
if ((pid = fork()) == 0) {
// Fork child process again
@ -63,6 +77,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
close(fd[0]);
if ((child = fork()) == 0) {
close(fd[1]);
if (ctx) {
export_xdga_token(ctx);
}
if (ctx && !no_startup_id) {
export_startup_id(ctx);
}
execlp("sh", "sh", "-c", cmd, (void *)NULL);
sway_log_errno(SWAY_ERROR, "execlp failed");
_exit(1);
@ -90,8 +110,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
waitpid(pid, NULL, 0);
if (child > 0) {
sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
root_record_workspace_pid(child);
if (ctx != NULL) {
sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
ctx->pid = child;
}
} else {
launcher_ctx_destroy(ctx);
return cmd_results_new(CMD_FAILURE, "Second fork() failed");
}

View file

@ -285,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
}
} else {
return cmd_results_new(CMD_FAILURE,
"Failed to find a %s container in workspace",
"Failed to find a %s container in workspace.",
floating ? "floating" : "tiling");
}
return cmd_results_new(CMD_SUCCESS, NULL);
@ -295,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
int argc, char **argv) {
if (!argc) {
return cmd_results_new(CMD_INVALID,
"Expected 'focus output <direction|name>'");
"Expected 'focus output <direction|name>'.");
}
char *identifier = join_args(argv, argc);
struct sway_output *output = output_by_name_or_id(identifier);
@ -305,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
if (!parse_direction(identifier, &direction)) {
free(identifier);
return cmd_results_new(CMD_INVALID,
"There is no output with that name");
"There is no output with that name.");
}
struct sway_workspace *ws = seat_get_focused_workspace(seat);
if (!ws) {
free(identifier);
return cmd_results_new(CMD_FAILURE,
"No focused workspace to base directions off of");
"No focused workspace to base directions off of.");
}
output = output_get_in_direction(ws->output, direction);
@ -375,10 +375,14 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
struct sway_seat *seat = config->handler_context.seat;
if (node->type < N_WORKSPACE) {
return cmd_results_new(CMD_FAILURE,
"Command 'focus' cannot be used above the workspace level");
"Command 'focus' cannot be used above the workspace level.");
}
if (argc == 0) {
if (!container) {
return cmd_results_new(CMD_FAILURE, "No container to focus was specified.");
}
if (argc == 0 && container) {
if (container_is_scratchpad_hidden_or_child(container)) {
root_scratchpad_show(container);
}

View file

@ -4,6 +4,7 @@
#include "sway/config.h"
#include "log.h"
#include "stringop.h"
#include <pango/pangocairo.h>
struct cmd_results *cmd_font(int argc, char **argv) {
struct cmd_results *error = NULL;
@ -16,12 +17,34 @@ struct cmd_results *cmd_font(int argc, char **argv) {
if (strncmp(font, "pango:", 6) == 0) {
config->pango_markup = true;
config->font = strdup(font + 6);
free(font);
} else {
config->pango_markup = false;
config->font = strdup(font);
config->font = font;
}
free(font);
// Parse the font early so we can reject it if it's not valid for pango.
// Also avoids re-parsing each time we render text.
PangoFontDescription *font_description = pango_font_description_from_string(config->font);
const char *family = pango_font_description_get_family(font_description);
if (family == NULL) {
pango_font_description_free(font_description);
return cmd_results_new(CMD_FAILURE, "Invalid font family.");
}
const gint size = pango_font_description_get_size(font_description);
if (size == 0) {
pango_font_description_free(font_description);
return cmd_results_new(CMD_FAILURE, "Invalid font size.");
}
if (config->font_description != NULL) {
pango_font_description_free(config->font_description);
}
config->font_description = font_description;
config_update_font_height();
return cmd_results_new(CMD_SUCCESS, NULL);
}

166
sway/commands/gesture.c Normal file
View file

@ -0,0 +1,166 @@
#define _POSIX_C_SOURCE 200809L
#include "sway/config.h"
#include "gesture.h"
#include "log.h"
#include "stringop.h"
#include "sway/commands.h"
void free_gesture_binding(struct sway_gesture_binding *binding) {
if (!binding) {
return;
}
free(binding->input);
free(binding->command);
free(binding);
}
/**
* Returns true if the bindings have the same gesture type, direction, etc
*/
static bool binding_gesture_equal(struct sway_gesture_binding *binding_a,
struct sway_gesture_binding *binding_b) {
if (strcmp(binding_a->input, binding_b->input) != 0) {
return false;
}
if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {
return false;
}
if ((binding_a->flags & BINDING_EXACT) !=
(binding_b->flags & BINDING_EXACT)) {
return false;
}
return true;
}
/**
* Add gesture binding to config
*/
static struct cmd_results *gesture_binding_add(
struct sway_gesture_binding *binding,
const char *gesturecombo, bool warn) {
list_t *mode_bindings = config->current_mode->gesture_bindings;
// overwrite the binding if it already exists
bool overwritten = false;
for (int i = 0; i < mode_bindings->length; ++i) {
struct sway_gesture_binding *config_binding = mode_bindings->items[i];
if (binding_gesture_equal(binding, config_binding)) {
sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`",
gesturecombo, binding->command, config_binding->command);
if (warn) {
config_add_swaynag_warning("Overwriting binding"
"'%s' to `%s` from `%s`",
gesturecombo, binding->command,
config_binding->command);
}
free_gesture_binding(config_binding);
mode_bindings->items[i] = binding;
overwritten = true;
}
}
if (!overwritten) {
list_add(mode_bindings, binding);
sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`",
gesturecombo, binding->command);
}
return cmd_results_new(CMD_SUCCESS, NULL);
}
/**
* Remove gesture binding from config
*/
static struct cmd_results *gesture_binding_remove(
struct sway_gesture_binding *binding, const char *gesturecombo) {
list_t *mode_bindings = config->current_mode->gesture_bindings;
for (int i = 0; i < mode_bindings->length; ++i) {
struct sway_gesture_binding *config_binding = mode_bindings->items[i];
if (binding_gesture_equal(binding, config_binding)) {
free_gesture_binding(config_binding);
free_gesture_binding(binding);
list_del(mode_bindings, i);
sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture",
gesturecombo);
return cmd_results_new(CMD_SUCCESS, NULL);
}
}
free_gesture_binding(binding);
return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`",
gesturecombo);
}
/**
* Parse and execute bindgesture or unbindgesture command.
*/
static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {
int minargs = 2;
char *bindtype = "bindgesture";
if (unbind) {
minargs--;
bindtype = "unbindgesture";
}
struct cmd_results *error = NULL;
if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
return error;
}
struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
if (!binding) {
return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
}
binding->input = strdup("*");
bool warn = true;
// Handle flags
while (argc > 0) {
if (strcmp("--exact", argv[0]) == 0) {
binding->flags |= BINDING_EXACT;
} else if (strcmp("--no-warn", argv[0]) == 0) {
warn = false;
} else if (strncmp("--input-device=", argv[0],
strlen("--input-device=")) == 0) {
free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device="));
} else {
break;
}
argv++;
argc--;
}
if (argc < minargs) {
free(binding);
return cmd_results_new(CMD_FAILURE,
"Invalid %s command (expected at least %d "
"non-option arguments, got %d)", bindtype, minargs, argc);
}
char* errmsg = NULL;
if ((errmsg = gesture_parse(argv[0], &binding->gesture))) {
free(binding);
struct cmd_results *final = cmd_results_new(CMD_FAILURE,
"Invalid %s command (%s)",
bindtype, errmsg);
free(errmsg);
return final;
}
if (unbind) {
return gesture_binding_remove(binding, argv[0]);
}
binding->command = join_args(argv + 1, argc - 1);
return gesture_binding_add(binding, argv[0], warn);
}
struct cmd_results *cmd_bindgesture(int argc, char **argv) {
return cmd_bind_or_unbind_gesture(argc, argv, false);
}
struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
return cmd_bind_or_unbind_gesture(argc, argv, true);
}

View file

@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = {
{ "drag", input_cmd_drag },
{ "drag_lock", input_cmd_drag_lock },
{ "dwt", input_cmd_dwt },
{ "dwtp", input_cmd_dwtp },
{ "events", input_cmd_events },
{ "left_handed", input_cmd_left_handed },
{ "map_from_region", input_cmd_map_from_region },

View file

@ -0,0 +1,25 @@
#include <string.h>
#include <strings.h>
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
#include "util.h"
struct cmd_results *input_cmd_dwtp(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) {
return error;
}
struct input_config *ic = config->handler_context.input_config;
if (!ic) {
return cmd_results_new(CMD_FAILURE, "No input device defined.");
}
if (parse_boolean(argv[0], true)) {
ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED;
} else {
ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED;
}
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -1,10 +1,16 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
#include "log.h"
struct xkb_switch_layout_action {
struct wlr_keyboard *keyboard;
xkb_layout_index_t layout;
};
static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) {
xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
if (idx >= num_layouts) {
@ -28,10 +34,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) {
return layout_idx;
}
static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) {
static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) {
xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
xkb_layout_index_t idx = get_current_layout_index(kbd);
switch_layout(kbd, (idx + num_layouts + dir) % num_layouts);
return (idx + num_layouts + dir) % num_layouts;
}
struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
@ -66,6 +72,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
relative = 0;
}
struct xkb_switch_layout_action *actions = calloc(
wl_list_length(&server.input->devices),
sizeof(struct xkb_switch_layout_action));
size_t actions_len = 0;
if (!actions) {
return cmd_results_new(CMD_FAILURE, "Unable to allocate actions");
}
/* Calculate new indexes first because switching a layout in one
keyboard may result in a change on other keyboards as well because
of keyboard groups. */
struct sway_input_device *dev;
wl_list_for_each(dev, &server.input->devices, link) {
if (strcmp(ic->identifier, "*") != 0 &&
@ -76,12 +94,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
continue;
}
struct xkb_switch_layout_action *action =
&actions[actions_len++];
action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
if (relative) {
switch_layout_relative(dev->wlr_device->keyboard, relative);
action->layout = get_layout_relative(action->keyboard, relative);
} else {
switch_layout(dev->wlr_device->keyboard, layout);
action->layout = layout;
}
}
for (size_t i = 0; i < actions_len; i++) {
switch_layout(actions[i].keyboard, actions[i].layout);
}
free(actions);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -11,10 +11,12 @@
// Must be in order for the bsearch
static const struct cmd_handler mode_handlers[] = {
{ "bindcode", cmd_bindcode },
{ "bindgesture", cmd_bindgesture },
{ "bindswitch", cmd_bindswitch },
{ "bindsym", cmd_bindsym },
{ "set", cmd_set },
{ "unbindcode", cmd_unbindcode },
{ "unbindgesture", cmd_unbindgesture },
{ "unbindswitch", cmd_unbindswitch },
{ "unbindsym", cmd_unbindsym },
};
@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
mode->keycode_bindings = create_list();
mode->mouse_bindings = create_list();
mode->switch_bindings = create_list();
mode->gesture_bindings = create_list();
mode->pango = pango;
list_add(config->modes, mode);
}

View file

@ -686,6 +686,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
arrange_output(old_output);
arrange_output(new_output);
struct sway_seat *seat = config->handler_context.seat;
seat_consider_warp_to_focus(seat);
return cmd_results_new(CMD_SUCCESS, NULL);
}
@ -788,15 +791,15 @@ static struct cmd_results *cmd_move_to_position_pointer(
struct wlr_output *output = wlr_output_layout_output_at(
root->output_layout, cursor->x, cursor->y);
if (output) {
struct wlr_box *box =
wlr_output_layout_get_box(root->output_layout, output);
lx = fmax(lx, box->x);
ly = fmax(ly, box->y);
if (lx + container->pending.width > box->x + box->width) {
lx = box->x + box->width - container->pending.width;
struct wlr_box box;
wlr_output_layout_get_box(root->output_layout, output, &box);
lx = fmax(lx, box.x);
ly = fmax(ly, box.y);
if (lx + container->pending.width > box.x + box.width) {
lx = box.x + box.width - container->pending.width;
}
if (ly + container->pending.height > box->y + box->height) {
ly = box->y + box->height - container->pending.height;
if (ly + container->pending.height > box.y + box.height) {
ly = box.y + box.height - container->pending.height;
}
}

View file

@ -18,6 +18,7 @@ static const struct cmd_handler output_handlers[] = {
{ "modeline", output_cmd_modeline },
{ "pos", output_cmd_position },
{ "position", output_cmd_position },
{ "power", output_cmd_power },
{ "render_bit_depth", output_cmd_render_bit_depth },
{ "res", output_cmd_mode },
{ "resolution", output_cmd_mode },
@ -26,6 +27,7 @@ static const struct cmd_handler output_handlers[] = {
{ "subpixel", output_cmd_subpixel },
{ "toggle", output_cmd_toggle },
{ "transform", output_cmd_transform },
{ "unplug", output_cmd_unplug },
};
struct cmd_results *cmd_output(int argc, char **argv) {

View file

@ -102,19 +102,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
}
char *conf_path = dirname(conf);
char *rel_path = src;
src = malloc(strlen(conf_path) + strlen(src) + 2);
if (!src) {
free(rel_path);
char *real_src = malloc(strlen(conf_path) + strlen(src) + 2);
if (!real_src) {
free(src);
free(conf);
sway_log(SWAY_ERROR, "Unable to allocate memory");
return cmd_results_new(CMD_FAILURE,
"Unable to allocate resources");
}
sprintf(src, "%s/%s", conf_path, rel_path);
free(rel_path);
snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src);
free(src);
free(conf);
src = real_src;
}
bool can_access = access(src, F_OK) != -1;

View file

@ -1,45 +1,8 @@
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
#include "util.h"
#include <strings.h>
struct cmd_results *output_cmd_dpms(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
if (!argc) {
return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
}
enum config_dpms current_dpms = DPMS_ON;
if (strcasecmp(argv[0], "toggle") == 0) {
const char *oc_name = config->handler_context.output_config->name;
if (strcmp(oc_name, "*") == 0) {
return cmd_results_new(CMD_INVALID,
"Cannot apply toggle to all outputs.");
}
struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
if (!sway_output || !sway_output->wlr_output) {
return cmd_results_new(CMD_FAILURE,
"Cannot apply toggle to unknown output %s", oc_name);
}
if (sway_output->enabled && !sway_output->wlr_output->enabled) {
current_dpms = DPMS_OFF;
}
}
if (parse_boolean(argv[0], current_dpms == DPMS_ON)) {
config->handler_context.output_config->dpms_state = DPMS_ON;
} else {
config->handler_context.output_config->dpms_state = DPMS_OFF;
}
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
return NULL;
sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, "
"use \"output power\" instead");
return output_cmd_power(argc, argv);
}

View file

@ -0,0 +1,43 @@
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
#include "util.h"
struct cmd_results *output_cmd_power(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
if (argc == 0) {
return cmd_results_new(CMD_INVALID, "Missing power argument");
}
bool current = true;
if (strcasecmp(argv[0], "toggle") == 0) {
const char *oc_name = config->handler_context.output_config->name;
if (strcmp(oc_name, "*") == 0) {
return cmd_results_new(CMD_INVALID,
"Cannot apply toggle to all outputs");
}
struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
if (!sway_output || !sway_output->wlr_output) {
return cmd_results_new(CMD_FAILURE,
"Cannot apply toggle to unknown output %s", oc_name);
}
if (sway_output->enabled && !sway_output->wlr_output->enabled) {
current = false;
}
}
if (parse_boolean(argv[0], current)) {
config->handler_context.output_config->power = 1;
} else {
config->handler_context.output_config->power = 0;
}
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
return NULL;
}

View file

@ -0,0 +1,54 @@
#include <strings.h>
#include <wlr/config.h>
#include <wlr/backend/headless.h>
#include <wlr/backend/wayland.h>
#if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h>
#endif
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
static bool is_backend_allowed(struct wlr_backend *backend) {
if (wlr_backend_is_headless(backend)) {
return true;
}
if (wlr_backend_is_wl(backend)) {
return true;
}
#if WLR_HAS_X11_BACKEND
if (wlr_backend_is_x11(backend)) {
return true;
}
#endif
return false;
}
/**
* This command is intended for developer use only.
*/
struct cmd_results *output_cmd_unplug(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
const char *oc_name = config->handler_context.output_config->name;
if (strcmp(oc_name, "*") == 0) {
return cmd_results_new(CMD_INVALID, "Won't unplug all outputs");
}
struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
if (!sway_output) {
return cmd_results_new(CMD_INVALID,
"Cannot unplug unknown output %s", oc_name);
}
if (!is_backend_allowed(sway_output->wlr_output->backend)) {
return cmd_results_new(CMD_INVALID,
"Can only unplug outputs with headless, wayland or x11 backend");
}
wlr_output_destroy(sway_output->wlr_output);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -7,6 +7,7 @@
#include "sway/config.h"
#include "sway/ipc-server.h"
#include "sway/output.h"
#include "sway/desktop/launcher.h"
#include "sway/tree/container.h"
#include "sway/tree/workspace.h"
#include "sway/tree/root.h"
@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
root_rename_pid_workspaces(workspace->name, new_name);
free(workspace->name);
workspace->name = new_name;

View file

@ -111,8 +111,8 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
: WLR_AXIS_ORIENTATION_HORIZONTAL;
double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
? -1 : 1;
struct wlr_event_pointer_axis event = {
.device = NULL,
struct wlr_pointer_axis_event event = {
.pointer = NULL,
.time_msec = 0,
.source = WLR_AXIS_SOURCE_WHEEL,
.orientation = orientation,

View file

@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) {
}
list_free(mode->switch_bindings);
}
if (mode->gesture_bindings) {
for (int i = 0; i < mode->gesture_bindings->length; i++) {
free_gesture_binding(mode->gesture_bindings->items[i]);
}
list_free(mode->gesture_bindings);
}
free(mode);
}
@ -222,6 +228,7 @@ static void config_defaults(struct sway_config *config) {
if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
list_add(config->modes, config->current_mode);
config->floating_mod = 0;
@ -236,6 +243,7 @@ static void config_defaults(struct sway_config *config) {
config->default_layout = L_NONE;
config->default_orientation = L_NONE;
if (!(config->font = strdup("monospace 10"))) goto cleanup;
config->font_description = pango_font_description_from_string(config->font);
config->urgent_timeout = 500;
config->focus_on_window_activation = FOWA_URGENT;
config->popup_during_fullscreen = POPUP_SMART;
@ -1005,7 +1013,7 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
void config_update_font_height(void) {
int prev_max_height = config->font_height;
get_text_metrics(config->font, &config->font_height, &config->font_baseline);
get_text_metrics(config->font_description, &config->font_height, &config->font_baseline);
if (config->font_height != prev_max_height) {
arrange_root();

View file

@ -25,6 +25,7 @@ struct input_config *new_input_config(const char* identifier) {
input->drag = INT_MIN;
input->drag_lock = INT_MIN;
input->dwt = INT_MIN;
input->dwtp = INT_MIN;
input->send_events = INT_MIN;
input->click_method = INT_MIN;
input->middle_emulation = INT_MIN;
@ -61,6 +62,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
if (src->dwt != INT_MIN) {
dst->dwt = src->dwt;
}
if (src->dwtp != INT_MIN) {
dst->dwtp = src->dwtp;
}
if (src->left_handed != INT_MIN) {
dst->left_handed = src->left_handed;
}

View file

@ -27,8 +27,10 @@ int output_name_cmp(const void *item, const void *data) {
void output_get_identifier(char *identifier, size_t len,
struct sway_output *output) {
struct wlr_output *wlr_output = output->wlr_output;
snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model,
wlr_output->serial);
snprintf(identifier, len, "%s %s %s",
wlr_output->make ? wlr_output->make : "Unknown",
wlr_output->model ? wlr_output->model : "Unknown",
wlr_output->serial ? wlr_output->serial : "Unknown");
}
const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
@ -69,6 +71,7 @@ struct output_config *new_output_config(const char *name) {
oc->max_render_time = -1;
oc->adaptive_sync = -1;
oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
oc->power = -1;
return oc;
}
@ -130,8 +133,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
free(dst->background_fallback);
dst->background_fallback = strdup(src->background_fallback);
}
if (src->dpms_state != 0) {
dst->dpms_state = src->dpms_state;
if (src->power != -1) {
dst->power = src->power;
}
}
@ -190,11 +193,11 @@ static void merge_id_on_name(struct output_config *oc) {
list_add(config->output_configs, ion_oc);
sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
" (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
"transform %d) (bg %s %s) (dpms %d) (max render time: %d)",
"transform %d) (bg %s %s) (power %d) (max render time: %d)",
ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
ion_oc->transform, ion_oc->background,
ion_oc->background_option, ion_oc->dpms_state,
ion_oc->background_option, ion_oc->power,
ion_oc->max_render_time);
}
}
@ -235,18 +238,18 @@ struct output_config *store_output_config(struct output_config *oc) {
}
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) "
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
"(max render time: %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->transform, oc->background, oc->background_option, oc->dpms_state,
oc->transform, oc->background, oc->background_option, oc->power,
oc->max_render_time);
return oc;
}
static void set_mode(struct wlr_output *output, int width, int height,
float refresh_rate, bool custom) {
static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
int width, int height, float refresh_rate, bool custom) {
// Not all floating point integers can be represented exactly
// as (int)(1000 * mHz / 1000.f)
// round() the result to avoid any error
@ -254,7 +257,7 @@ static void set_mode(struct wlr_output *output, int width, int height,
if (wl_list_empty(&output->modes) || custom) {
sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
wlr_output_set_custom_mode(output, width, height,
wlr_output_state_set_custom_mode(pending, width, height,
refresh_rate > 0 ? mhz : 0);
return;
}
@ -278,10 +281,11 @@ static void set_mode(struct wlr_output *output, int width, int height,
} else {
sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name);
}
wlr_output_set_mode(output, best);
wlr_output_state_set_mode(pending, best);
}
static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
static void set_modeline(struct wlr_output *output,
struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
if (!wlr_output_is_drm(output)) {
sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
return;
@ -289,7 +293,7 @@ static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
if (mode) {
wlr_output_set_mode(output, mode);
wlr_output_state_set_mode(pending, mode);
}
}
@ -311,23 +315,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
// 1 inch = 25.4 mm
#define MM_PER_INCH 25.4
static int compute_default_scale(struct wlr_output *output) {
static int compute_default_scale(struct wlr_output *output,
struct wlr_output_state *pending) {
struct wlr_box box = { .width = output->width, .height = output->height };
if (output->pending.committed & WLR_OUTPUT_STATE_MODE) {
switch (output->pending.mode_type) {
if (pending->committed & WLR_OUTPUT_STATE_MODE) {
switch (pending->mode_type) {
case WLR_OUTPUT_STATE_MODE_FIXED:
box.width = output->pending.mode->width;
box.height = output->pending.mode->height;
box.width = pending->mode->width;
box.height = pending->mode->height;
break;
case WLR_OUTPUT_STATE_MODE_CUSTOM:
box.width = output->pending.custom_mode.width;
box.height = output->pending.custom_mode.height;
box.width = pending->custom_mode.width;
box.height = pending->custom_mode.height;
break;
}
}
enum wl_output_transform transform = output->transform;
if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) {
transform = output->pending.transform;
if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
transform = pending->transform;
}
wlr_box_transform(&box, &box, transform, box.width, box.height);
@ -374,38 +379,38 @@ static const uint32_t *bit_depth_preferences[] = {
};
static void queue_output_config(struct output_config *oc,
struct sway_output *output) {
struct sway_output *output, struct wlr_output_state *pending) {
if (output == root->fallback_output) {
return;
}
struct wlr_output *wlr_output = output->wlr_output;
if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) {
if (oc && (!oc->enabled || oc->power == 0)) {
sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
wlr_output_enable(wlr_output, false);
wlr_output_state_set_enabled(pending, false);
return;
}
sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
wlr_output_enable(wlr_output, true);
wlr_output_state_set_enabled(pending, true);
if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
sway_log(SWAY_DEBUG, "Set %s modeline",
wlr_output->name);
set_modeline(wlr_output, &oc->drm_mode);
set_modeline(wlr_output, pending, &oc->drm_mode);
} else if (oc && oc->width > 0 && oc->height > 0) {
sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
wlr_output->name, oc->width, oc->height, oc->refresh_rate);
set_mode(wlr_output, oc->width, oc->height,
set_mode(wlr_output, pending, oc->width, oc->height,
oc->refresh_rate, oc->custom_mode == 1);
} else if (!wl_list_empty(&wlr_output->modes)) {
sway_log(SWAY_DEBUG, "Set preferred mode");
struct wlr_output_mode *preferred_mode =
wlr_output_preferred_mode(wlr_output);
wlr_output_set_mode(wlr_output, preferred_mode);
wlr_output_state_set_mode(pending, preferred_mode);
if (!wlr_output_test(wlr_output)) {
if (!wlr_output_test_state(wlr_output, pending)) {
sway_log(SWAY_DEBUG, "Preferred mode rejected, "
"falling back to another mode");
struct wlr_output_mode *mode;
@ -414,8 +419,8 @@ static void queue_output_config(struct output_config *oc,
continue;
}
wlr_output_set_mode(wlr_output, mode);
if (wlr_output_test(wlr_output)) {
wlr_output_state_set_mode(pending, mode);
if (wlr_output_test_state(wlr_output, pending)) {
break;
}
}
@ -425,7 +430,7 @@ static void queue_output_config(struct output_config *oc,
if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
sway_wl_output_subpixel_to_string(oc->subpixel));
wlr_output_set_subpixel(wlr_output, oc->subpixel);
wlr_output_state_set_subpixel(pending, oc->subpixel);
}
enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
@ -437,7 +442,7 @@ static void queue_output_config(struct output_config *oc,
}
if (wlr_output->transform != tr) {
sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
wlr_output_set_transform(wlr_output, tr);
wlr_output_state_set_transform(pending, tr);
}
// Apply the scale last before the commit, because the scale auto-detection
@ -446,18 +451,22 @@ static void queue_output_config(struct output_config *oc,
if (oc && oc->scale > 0) {
scale = oc->scale;
} else {
scale = compute_default_scale(wlr_output);
scale = compute_default_scale(wlr_output, pending);
sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
}
if (scale != wlr_output->scale) {
sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
wlr_output_set_scale(wlr_output, scale);
wlr_output_state_set_scale(pending, scale);
}
if (oc && oc->adaptive_sync != -1) {
sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
oc->adaptive_sync);
wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1);
wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
wlr_output_state_set_adaptive_sync_enabled(pending, false);
}
}
if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
@ -465,8 +474,8 @@ static void queue_output_config(struct output_config *oc,
assert(fmts);
for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) {
wlr_output_set_render_format(wlr_output, fmts[i]);
if (wlr_output_test(wlr_output)) {
wlr_output_state_set_render_format(pending, fmts[i]);
if (wlr_output_test_state(wlr_output, pending)) {
break;
}
@ -487,14 +496,15 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
// Flag to prevent the output mode event handler from calling us
output->enabling = (!oc || oc->enabled);
queue_output_config(oc, output);
struct wlr_output_state pending = {0};
queue_output_config(oc, output, &pending);
if (!oc || oc->dpms_state != DPMS_OFF) {
output->current_mode = wlr_output->pending.mode;
if (!oc || oc->power != 0) {
output->current_mode = pending.mode;
}
sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
if (!wlr_output_commit(wlr_output)) {
if (!wlr_output_commit_state(wlr_output, &pending)) {
// Failed to commit output changes, maybe the output is missing a CRTC.
// Leave the output disabled for now and try again when the output gets
// the mode we asked for.
@ -546,12 +556,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
}
// Update output->{lx, ly, width, height}
struct wlr_box *output_box =
wlr_output_layout_get_box(root->output_layout, wlr_output);
output->lx = output_box->x;
output->ly = output_box->y;
output->width = output_box->width;
output->height = output_box->height;
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
output->lx = output_box.x;
output->ly = output_box.y;
output->width = output_box.width;
output->height = output_box.height;
if (!output->enabled) {
output_enable(output);
@ -577,15 +587,15 @@ bool test_output_config(struct output_config *oc, struct sway_output *output) {
return false;
}
queue_output_config(oc, output);
bool ok = wlr_output_test(output->wlr_output);
wlr_output_rollback(output->wlr_output);
return ok;
struct wlr_output_state pending = {0};
queue_output_config(oc, output, &pending);
return wlr_output_test_state(output->wlr_output, &pending);
}
static void default_output_config(struct output_config *oc,
struct wlr_output *wlr_output) {
oc->enabled = 1;
oc->power = 1;
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
if (mode != NULL) {
oc->width = mode->width;
@ -598,7 +608,6 @@ static void default_output_config(struct output_config *oc,
struct sway_output *output = wlr_output->data;
oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
oc->dpms_state = DPMS_ON;
oc->max_render_time = 0;
}
@ -653,10 +662,10 @@ static struct output_config *get_output_config(char *identifier,
sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
" (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
" (dpms %d) (max render time: %d)", result->name, result->enabled,
" (power %d) (max render time: %d)", result->name, result->enabled,
result->width, result->height, result->refresh_rate,
result->x, result->y, result->scale, result->transform,
result->background, result->background_option, result->dpms_state,
result->background, result->background_option, result->power,
result->max_render_time);
} else if (oc_name) {
// No identifier config, just return a copy of the name config

View file

@ -3,7 +3,8 @@
#include <stdio.h>
#include <stdbool.h>
#include <strings.h>
#include <pcre.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "sway/criteria.h"
#include "sway/tree/container.h"
#include "sway/config.h"
@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) {
char *error = NULL;
// Returns error string on failure or NULL otherwise.
static bool generate_regex(pcre **regex, char *value) {
const char *reg_err;
int offset;
*regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
static bool generate_regex(pcre2_code **regex, char *value) {
int errorcode;
PCRE2_SIZE offset;
*regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL);
if (!*regex) {
PCRE2_UCHAR buffer[256];
pcre2_get_error_message(errorcode, buffer, sizeof(buffer));
const char *fmt = "Regex compilation for '%s' failed: %s";
int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3;
int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3;
error = malloc(len);
snprintf(error, len, fmt, value, reg_err);
snprintf(error, len, fmt, value, buffer);
return false;
}
@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
if (strcmp(value, "__focused__") == 0) {
(*pattern)->match_type = PATTERN_FOCUSED;
} else {
(*pattern)->match_type = PATTERN_PCRE;
(*pattern)->match_type = PATTERN_PCRE2;
if (!generate_regex(&(*pattern)->regex, value)) {
return false;
};
@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
static void pattern_destroy(struct pattern *pattern) {
if (pattern) {
if (pattern->regex) {
pcre_free(pattern->regex);
pcre2_code_free(pattern->regex);
}
free(pattern);
}
@ -93,14 +96,18 @@ void criteria_destroy(struct criteria *criteria) {
pattern_destroy(criteria->window_role);
#endif
pattern_destroy(criteria->con_mark);
free(criteria->workspace);
pattern_destroy(criteria->workspace);
free(criteria->target);
free(criteria->cmdlist);
free(criteria->raw);
free(criteria);
}
static int regex_cmp(const char *item, const pcre *regex) {
return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
static int regex_cmp(const char *item, const pcre2_code *regex) {
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL);
pcre2_match_data_free(match_data);
return result;
}
#if HAVE_XWAYLAND
@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria,
bool exists = false;
struct sway_container *con = container;
for (int i = 0; i < con->marks->length; ++i) {
if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) {
if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) {
exists = true;
break;
}
@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->title) {
const char *title = view_get_title(view);
if (!title) {
return false;
title = "";
}
switch (criteria->title->match_type) {
@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false;
}
break;
case PATTERN_PCRE:
if (regex_cmp(title, criteria->title->regex) != 0) {
case PATTERN_PCRE2:
if (regex_cmp(title, criteria->title->regex) < 0) {
return false;
}
break;
@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->shell) {
const char *shell = view_get_shell(view);
if (!shell) {
return false;
shell = "";
}
switch (criteria->shell->match_type) {
@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false;
}
break;
case PATTERN_PCRE:
if (regex_cmp(shell, criteria->shell->regex) != 0) {
case PATTERN_PCRE2:
if (regex_cmp(shell, criteria->shell->regex) < 0) {
return false;
}
break;
@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->app_id) {
const char *app_id = view_get_app_id(view);
if (!app_id) {
return false;
app_id = "";
}
switch (criteria->app_id->match_type) {
@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false;
}
break;
case PATTERN_PCRE:
if (regex_cmp(app_id, criteria->app_id->regex) != 0) {
case PATTERN_PCRE2:
if (regex_cmp(app_id, criteria->app_id->regex) < 0) {
return false;
}
break;
@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->class) {
const char *class = view_get_class(view);
if (!class) {
return false;
class = "";
}
switch (criteria->class->match_type) {
@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false;
}
break;
case PATTERN_PCRE:
if (regex_cmp(class, criteria->class->regex) != 0) {
case PATTERN_PCRE2:
if (regex_cmp(class, criteria->class->regex) < 0) {
return false;
}
break;
@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->instance) {
const char *instance = view_get_instance(view);
if (!instance) {
return false;
instance = "";
}
switch (criteria->instance->match_type) {
case PATTERN_FOCUSED:
if (focused && strcmp(instance, view_get_instance(focused))) {
if (focused && lenient_strcmp(instance, view_get_instance(focused))) {
return false;
}
break;
case PATTERN_PCRE:
if (regex_cmp(instance, criteria->instance->regex) != 0) {
case PATTERN_PCRE2:
if (regex_cmp(instance, criteria->instance->regex) < 0) {
return false;
}
break;
@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->window_role) {
const char *window_role = view_get_window_role(view);
if (!window_role) {
return false;
window_role = "";
}
switch (criteria->window_role->match_type) {
case PATTERN_FOCUSED:
if (focused && strcmp(window_role, view_get_window_role(focused))) {
if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) {
return false;
}
break;
case PATTERN_PCRE:
if (regex_cmp(window_role, criteria->window_role->regex) != 0) {
case PATTERN_PCRE2:
if (regex_cmp(window_role, criteria->window_role->regex) < 0) {
return false;
}
break;
@ -363,8 +370,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false;
}
break;
case PATTERN_PCRE:
if (regex_cmp(ws->name, criteria->workspace->regex) != 0) {
case PATTERN_PCRE2:
if (regex_cmp(ws->name, criteria->workspace->regex) < 0) {
return false;
}
break;
@ -676,7 +683,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
}
name = calloc(head - namestart + 1, 1);
if (head != namestart) {
strncpy(name, namestart, head - namestart);
memcpy(name, namestart, head - namestart);
}
// Parse token value
skip_spaces(&head);
@ -703,7 +710,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
}
}
value = calloc(head - valuestart + 1, 1);
strncpy(value, valuestart, head - valuestart);
memcpy(value, valuestart, head - valuestart);
if (in_quotes) {
++head;
in_quotes = false;
@ -734,7 +741,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
++head;
int len = head - raw;
criteria->raw = calloc(len + 1, 1);
strncpy(criteria->raw, raw, len);
memcpy(criteria->raw, raw, len);
return criteria;
cleanup:

View file

@ -6,10 +6,11 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
bool whole) {
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
struct wlr_box *output_box = wlr_output_layout_get_box(
root->output_layout, output->wlr_output);
output_damage_surface(output, lx - output_box->x,
ly - output_box->y, surface, whole);
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout,
output->wlr_output, &output_box);
output_damage_surface(output, lx - output_box.x,
ly - output_box.y, surface, whole);
}
}

View file

@ -35,6 +35,71 @@ static const GLfloat verts[] = {
0, 1, // bottom left
};
static const float transforms[][9] = {
[WL_OUTPUT_TRANSFORM_NORMAL] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_90] = {
0.0f, 1.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_180] = {
-1.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_270] = {
0.0f, -1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED] = {
-1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_90] = {
0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_180] = {
1.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_270] = {
0.0f, -1.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
};
static void matrix_projection(float mat[static 9], int width, int height,
enum wl_output_transform transform) {
memset(mat, 0, sizeof(*mat) * 9);
const float *t = transforms[transform];
float x = 2.0f / width;
float y = 2.0f / height;
// Rotation + reflection
mat[0] = x * t[0];
mat[1] = x * t[1];
mat[3] = y * -t[3];
mat[4] = y * -t[4];
// Translation
mat[2] = -copysign(1.0f, mat[0] + mat[1]);
mat[5] = -copysign(1.0f, mat[3] + mat[4]);
// Identity
mat[8] = 1.0f;
}
static GLuint compile_shader(GLuint type, const GLchar *src) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, NULL);
@ -156,7 +221,8 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {
// TODO: wlr_egl_make_current or eglMakeCurrent?
// TODO: assert instead of conditional statement?
if (!wlr_egl_make_current(egl)) {
if (!eglMakeCurrent(wlr_egl_get_display(egl), EGL_NO_SURFACE, EGL_NO_SURFACE,
wlr_egl_get_context(egl))) {
sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not make EGL current");
return NULL;
}
@ -242,7 +308,11 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {
goto error;
}
wlr_egl_unset_current(renderer->egl);
if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl),
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL");
goto error;
}
sway_log(SWAY_INFO, "GLES2 RENDERER: Shaders Initialized Successfully");
return renderer;
@ -257,7 +327,10 @@ error:
glDeleteProgram(renderer->shaders.tex_rgbx.program);
glDeleteProgram(renderer->shaders.tex_ext.program);
wlr_egl_unset_current(renderer->egl);
if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl),
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL");
}
// TODO: more freeing?
free(renderer);
@ -270,7 +343,7 @@ void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t he
glViewport(0, 0, width, height);
// refresh projection matrix
wlr_matrix_projection(renderer->projection, width, height,
matrix_projection(renderer->projection, width, height,
WL_OUTPUT_TRANSFORM_FLIPPED_180);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

View file

@ -1,5 +1,6 @@
#include <stdlib.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include "log.h"
#include "sway/desktop/idle_inhibit_v1.h"
#include "sway/input/seat.h"
@ -140,6 +141,7 @@ void sway_idle_inhibit_v1_check_active(
}
}
wlr_idle_set_enabled(manager->idle, NULL, !inhibited);
wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited);
}
struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create(

211
sway/desktop/launcher.c Normal file
View file

@ -0,0 +1,211 @@
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/desktop/launcher.h"
#include "sway/tree/node.h"
#include "sway/tree/container.h"
#include "sway/tree/workspace.h"
#include "sway/tree/root.h"
#include "log.h"
/**
* Get the pid of a parent process given the pid of a child process.
*
* Returns the parent pid or NULL if the parent pid cannot be determined.
*/
static pid_t get_parent_pid(pid_t child) {
pid_t parent = -1;
char file_name[100];
char *buffer = NULL;
const char *sep = " ";
FILE *stat = NULL;
size_t buf_size = 0;
snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
if ((stat = fopen(file_name, "r"))) {
if (getline(&buffer, &buf_size, stat) != -1) {
strtok(buffer, sep); // pid
strtok(NULL, sep); // executable name
strtok(NULL, sep); // state
char *token = strtok(NULL, sep); // parent pid
parent = strtol(token, NULL, 10);
}
free(buffer);
fclose(stat);
}
if (parent) {
return (parent == child) ? -1 : parent;
}
return -1;
}
void launcher_ctx_consume(struct launcher_ctx *ctx) {
// The view is now responsible for destroying this ctx
wl_list_remove(&ctx->token_destroy.link);
wl_list_init(&ctx->token_destroy.link);
if (!ctx->activated) {
// An unactivated token hasn't been destroyed yet
wlr_xdg_activation_token_v1_destroy(ctx->token);
}
ctx->token = NULL;
// Prevent additional matches
wl_list_remove(&ctx->link);
wl_list_init(&ctx->link);
}
void launcher_ctx_destroy(struct launcher_ctx *ctx) {
if (ctx == NULL) {
return;
}
wl_list_remove(&ctx->node_destroy.link);
wl_list_remove(&ctx->token_destroy.link);
wl_list_remove(&ctx->link);
wlr_xdg_activation_token_v1_destroy(ctx->token);
free(ctx->name);
free(ctx);
}
struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) {
if (wl_list_empty(&server.pending_launcher_ctxs)) {
return NULL;
}
struct launcher_ctx *ctx = NULL;
sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
do {
struct launcher_ctx *_ctx = NULL;
wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) {
if (pid == _ctx->pid) {
ctx = _ctx;
sway_log(SWAY_DEBUG,
"found %s match for pid %d: %s",
node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node));
break;
}
}
pid = get_parent_pid(pid);
} while (pid > 1);
return ctx;
}
struct sway_workspace *launcher_ctx_get_workspace(
struct launcher_ctx *ctx) {
struct sway_workspace *ws = NULL;
struct sway_output *output = NULL;
switch (ctx->node->type) {
case N_CONTAINER:
// Unimplemented
// TODO: add container matching?
ws = ctx->node->sway_container->pending.workspace;
break;
case N_WORKSPACE:
ws = ctx->node->sway_workspace;
break;
case N_OUTPUT:
output = ctx->node->sway_output;
ws = workspace_by_name(ctx->name);
if (!ws) {
sway_log(SWAY_DEBUG,
"Creating workspace %s for pid %d because it disappeared",
ctx->name, ctx->pid);
if (!output->enabled) {
sway_log(SWAY_DEBUG,
"Workspace output %s is disabled, trying another one",
output->wlr_output->name);
output = NULL;
}
ws = workspace_create(output, ctx->name);
}
break;
case N_ROOT:
ws = workspace_create(NULL, ctx->name);
break;
}
return ws;
}
static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) {
struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy);
switch (ctx->node->type) {
case N_CONTAINER:
// Unimplemented
break;
case N_WORKSPACE:;
struct sway_workspace *ws = ctx->node->sway_workspace;
wl_list_remove(&ctx->node_destroy.link);
wl_list_init(&ctx->node_destroy.link);
// We want to save this ws name to recreate later, hopefully on the
// same output
free(ctx->name);
ctx->name = strdup(ws->name);
if (!ws->output || ws->output->node.destroying) {
// If the output is being destroyed it would be pointless to track
// If the output is being disabled, we'll find out if it's still
// disabled when we try to match it.
ctx->node = &root->node;
break;
}
ctx->node = &ws->output->node;
wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
break;
case N_OUTPUT:
wl_list_remove(&ctx->node_destroy.link);
wl_list_init(&ctx->node_destroy.link);
// We'll make the ws ctx->name somewhere else
ctx->node = &root->node;
break;
case N_ROOT:
// Unreachable
break;
}
}
static void token_handle_destroy(struct wl_listener *listener, void *data) {
struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy);
ctx->token = NULL;
launcher_ctx_destroy(ctx);
}
struct launcher_ctx *launcher_ctx_create() {
struct sway_seat *seat = input_manager_current_seat();
struct sway_workspace *ws = seat_get_focused_workspace(seat);
if (!ws) {
sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace.");
return NULL;
}
struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx));
struct wlr_xdg_activation_token_v1 *token =
wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
token->data = ctx;
ctx->name = strdup(ws->name);
ctx->token = token;
ctx->node = &ws->node;
ctx->node_destroy.notify = ctx_handle_node_destroy;
wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
ctx->token_destroy.notify = token_handle_destroy;
wl_signal_add(&token->events.destroy, &ctx->token_destroy);
wl_list_init(&ctx->link);
wl_list_insert(&server.pending_launcher_ctxs, &ctx->link);
return ctx;
}
const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {
const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);
return token;
}

View file

@ -3,8 +3,8 @@
#include <string.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_subcompositor.h>
#include "log.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
@ -270,10 +270,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
wl_resource_get_client(sway_layer->layer_surface->resource);
bool set_focus = seat->exclusive_client == client;
wl_list_remove(&sway_layer->output_destroy.link);
wl_list_remove(&sway_layer->link);
wl_list_init(&sway_layer->link);
if (set_focus) {
struct sway_layer_surface *layer =
find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
@ -282,7 +278,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
}
}
sway_layer->layer_surface->output = NULL;
wlr_layer_surface_v1_destroy(sway_layer->layer_surface);
}
@ -291,10 +286,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
wl_container_of(listener, layer, surface_commit);
struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
struct wlr_output *wlr_output = layer_surface->output;
if (wlr_output == NULL) {
return;
}
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
struct wlr_box old_extent = layer->extent;
@ -341,13 +333,8 @@ static void unmap(struct sway_layer_surface *sway_layer) {
cursor_rebase_all();
struct wlr_output *wlr_output = sway_layer->layer_surface->output;
if (wlr_output == NULL) {
return;
}
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
if (output == NULL) {
return;
}
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
sway_layer->layer_surface->surface, true);
}
@ -375,22 +362,24 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&sway_layer->surface_commit.link);
wl_list_remove(&sway_layer->new_popup.link);
wl_list_remove(&sway_layer->new_subsurface.link);
if (sway_layer->layer_surface->output != NULL) {
struct sway_output *output = sway_layer->layer_surface->output->data;
if (output != NULL) {
struct wlr_output *wlr_output = sway_layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
arrange_layers(output);
transaction_commit_dirty();
}
wl_list_remove(&sway_layer->output_destroy.link);
sway_layer->layer_surface->output = NULL;
}
free(sway_layer);
}
static void handle_map(struct wl_listener *listener, void *data) {
struct sway_layer_surface *sway_layer = wl_container_of(listener,
sway_layer, map);
struct sway_output *output = sway_layer->layer_surface->output->data;
struct wlr_output *wlr_output = sway_layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
sway_layer->layer_surface->surface, true);
wlr_surface_send_enter(sway_layer->layer_surface->surface,
@ -408,9 +397,7 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface,
bool whole) {
struct sway_layer_surface *layer = subsurface->layer_surface;
struct wlr_output *wlr_output = layer->layer_surface->output;
if (!wlr_output) {
return;
}
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
@ -496,15 +483,15 @@ static struct sway_layer_surface *popup_get_layer(
static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) {
struct wlr_xdg_popup *popup = layer_popup->wlr_popup;
struct wlr_surface *surface = popup->base->surface;
int popup_sx = popup->geometry.x - popup->base->current.geometry.x;
int popup_sy = popup->geometry.y - popup->base->current.geometry.y;
int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x;
int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y;
int ox = popup_sx, oy = popup_sy;
struct sway_layer_surface *layer;
while (true) {
if (layer_popup->parent_type == LAYER_PARENT_POPUP) {
layer_popup = layer_popup->parent_popup;
ox += layer_popup->wlr_popup->geometry.x;
oy += layer_popup->wlr_popup->geometry.y;
ox += layer_popup->wlr_popup->current.geometry.x;
oy += layer_popup->wlr_popup->current.geometry.y;
} else {
layer = layer_popup->parent_layer;
ox += layer->geo.x;
@ -513,6 +500,7 @@ static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) {
}
}
struct wlr_output *wlr_output = layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
output_damage_surface(output, ox, oy, surface, whole);
}
@ -521,6 +509,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) {
struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
struct sway_layer_surface *layer = popup_get_layer(popup);
struct wlr_output *wlr_output = layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
popup_damage(popup, true);
}
@ -550,7 +539,9 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
struct sway_layer_surface *layer = popup_get_layer(popup);
struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
struct sway_output *output = layer->layer_surface->output->data;
struct wlr_output *wlr_output = layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
// the output box expressed in the coordinate system of the toplevel parent
// of the popup
@ -642,6 +633,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
sway_log(SWAY_ERROR,
"no output to auto-assign layer surface '%s' to",
layer_surface->namespace);
// Note that layer_surface->output can be NULL
// here, but none of our destroy callbacks are
// registered yet so we don't have to make them
// handle that case.
wlr_layer_surface_v1_destroy(layer_surface);
return;
}

View file

@ -10,11 +10,10 @@
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_drm_lease_v1.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/util/region.h>
#include "config.h"
#include "log.h"
@ -276,6 +275,25 @@ static void for_each_surface_container_iterator(struct sway_container *con,
static void output_for_each_surface(struct sway_output *output,
sway_surface_iterator_func_t iterator, void *user_data) {
if (server.session_lock.locked) {
if (server.session_lock.lock == NULL) {
return;
}
struct wlr_session_lock_surface_v1 *lock_surface;
wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
if (lock_surface->output != output->wlr_output) {
continue;
}
if (!lock_surface->mapped) {
continue;
}
output_surface_for_each_surface(output, lock_surface->surface,
0.0, 0.0, iterator, user_data);
}
return;
}
if (output_has_opaque_overlay_layer_surface(output)) {
goto overlay;
}
@ -435,6 +453,10 @@ static bool scan_out_fullscreen_view(struct sway_output *output,
return false;
}
if (server.session_lock.locked) {
return false;
}
if (!wl_list_empty(&view->saved_buffers)) {
return false;
}
@ -533,31 +555,43 @@ static int output_repaint_timer_handler(void *data) {
}
}
bool needs_frame;
pixman_region32_t damage;
pixman_region32_init(&damage);
if (!wlr_output_damage_attach_render(output->damage,
&needs_frame, &damage)) {
int buffer_age;
if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) {
return 0;
}
pixman_region32_t damage;
pixman_region32_init(&damage);
wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage);
if (!output->wlr_output->needs_frame &&
!pixman_region32_not_empty(&output->damage_ring.current)) {
pixman_region32_fini(&damage);
wlr_output_rollback(output->wlr_output);
return 0;
}
if (needs_frame) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
output_render(output, &now, &damage);
} else {
wlr_output_rollback(output->wlr_output);
}
pixman_region32_fini(&damage);
return 0;
}
static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
static void handle_damage(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, damage_frame);
wl_container_of(listener, output, damage);
struct wlr_output_event_damage *event = user_data;
if (wlr_damage_ring_add(&output->damage_ring, event->damage)) {
wlr_output_schedule_frame(output->wlr_output);
}
}
static void handle_frame(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, frame);
if (!output->enabled || !output->wlr_output->enabled) {
return;
}
@ -620,11 +654,18 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
send_frame_done(output, &data);
}
static void handle_needs_frame(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, needs_frame);
wlr_output_schedule_frame(output->wlr_output);
}
void output_damage_whole(struct sway_output *output) {
// The output can exist with no wlr_output if it's just been disconnected
// and the transaction to evacuate it has't completed yet.
if (output && output->wlr_output && output->damage) {
wlr_output_damage_add_whole(output->damage);
if (output != NULL && output->wlr_output != NULL) {
wlr_damage_ring_add_whole(&output->damage_ring);
wlr_output_schedule_frame(output->wlr_output);
}
}
@ -648,11 +689,15 @@ static void damage_surface_iterator(struct sway_output *output,
ceil(output->wlr_output->scale) - surface->current.scale);
}
pixman_region32_translate(&damage, box.x, box.y);
wlr_output_damage_add(output->damage, &damage);
if (wlr_damage_ring_add(&output->damage_ring, &damage)) {
wlr_output_schedule_frame(output->wlr_output);
}
pixman_region32_fini(&damage);
if (whole) {
wlr_output_damage_add_box(output->damage, &box);
if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
}
if (!wl_list_empty(&surface->current.frame_callback_list)) {
@ -682,7 +727,9 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
box.x -= output->lx;
box.y -= output->ly;
scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box);
if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
}
static void damage_child_views_iterator(struct sway_container *con,
@ -706,7 +753,9 @@ void output_damage_whole_container(struct sway_output *output,
.height = con->current.height + 2,
};
scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box);
if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
// Damage subsurfaces as well, which may extend outside the box
if (con->view) {
damage_child_views_iterator(con, output);
@ -715,20 +764,6 @@ void output_damage_whole_container(struct sway_output *output,
}
}
static void damage_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output =
wl_container_of(listener, output, damage_destroy);
if (!output->enabled) {
return;
}
output_disable(output);
wl_list_remove(&output->damage_destroy.link);
wl_list_remove(&output->damage_frame.link);
transaction_commit_dirty();
}
static void update_output_manager_config(struct sway_server *server) {
struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create();
@ -740,14 +775,15 @@ static void update_output_manager_config(struct sway_server *server) {
}
struct wlr_output_configuration_head_v1 *config_head =
wlr_output_configuration_head_v1_create(config, output->wlr_output);
struct wlr_box *output_box = wlr_output_layout_get_box(
root->output_layout, output->wlr_output);
// We mark the output enabled even if it is switched off by DPMS
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout,
output->wlr_output, &output_box);
// We mark the output enabled when it's switched off but not disabled
config_head->state.enabled = output->current_mode != NULL && output->enabled;
config_head->state.mode = output->current_mode;
if (output_box) {
config_head->state.x = output_box->x;
config_head->state.y = output_box->y;
if (!wlr_box_empty(&output_box)) {
config_head->state.x = output_box.x;
config_head->state.y = output_box.y;
}
}
@ -757,18 +793,24 @@ static void update_output_manager_config(struct sway_server *server) {
static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, destroy);
struct sway_server *server = output->server;
output_begin_destroy(output);
if (output->enabled) {
output_disable(output);
}
output_begin_destroy(output);
wl_list_remove(&output->link);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->commit.link);
wl_list_remove(&output->mode.link);
wl_list_remove(&output->present.link);
wl_list_remove(&output->damage.link);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->needs_frame.link);
wlr_damage_ring_finish(&output->damage_ring);
output->wlr_output->data = NULL;
output->wlr_output = NULL;
@ -796,10 +838,16 @@ static void handle_mode(struct wl_listener *listener, void *data) {
if (!output->enabled) {
return;
}
arrange_layers(output);
arrange_output(output);
transaction_commit_dirty();
int width, height;
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
wlr_output_schedule_frame(output->wlr_output);
update_output_manager_config(output->server);
}
@ -827,6 +875,13 @@ static void handle_commit(struct wl_listener *listener, void *data) {
update_output_manager_config(output->server);
}
if (event->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM)) {
int width, height;
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
wlr_output_schedule_frame(output->wlr_output);
}
}
static void handle_present(struct wl_listener *listener, void *data) {
@ -862,10 +917,12 @@ void handle_new_output(struct wl_listener *listener, void *data) {
if (wlr_output->non_desktop) {
sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output);
if (server->drm_lease_manager) {
wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
wlr_output);
}
list_add(root->non_desktop_outputs, non_desktop);
return;
}
@ -880,7 +937,7 @@ void handle_new_output(struct wl_listener *listener, void *data) {
return;
}
output->server = server;
output->damage = wlr_output_damage_create(wlr_output);
wlr_damage_ring_init(&output->damage_ring);
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->destroy.notify = handle_destroy;
@ -890,10 +947,12 @@ void handle_new_output(struct wl_listener *listener, void *data) {
output->mode.notify = handle_mode;
wl_signal_add(&wlr_output->events.present, &output->present);
output->present.notify = handle_present;
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
output->damage_frame.notify = damage_handle_frame;
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
output->damage_destroy.notify = damage_handle_destroy;
wl_signal_add(&wlr_output->events.damage, &output->damage);
output->damage.notify = handle_damage;
wl_signal_add(&wlr_output->events.frame, &output->frame);
output->frame.notify = handle_frame;
wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame);
output->needs_frame.notify = handle_needs_frame;
output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
output_repaint_timer_handler, output);
@ -1007,10 +1066,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
struct output_config *oc = new_output_config(output->wlr_output->name);
switch (event->mode) {
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
oc->dpms_state = DPMS_OFF;
oc->power = 0;
break;
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
oc->dpms_state = DPMS_ON;
oc->power = 1;
break;
}
oc = store_output_config(oc);

View file

@ -9,11 +9,11 @@
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/util/region.h>
#include "log.h"
#include "config.h"
@ -1301,6 +1301,41 @@ void output_render(struct sway_output *output, struct timespec *when,
fx_renderer_clear((float[]){1, 1, 0, 1});
}
if (server.session_lock.locked) {
float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
if (server.session_lock.lock == NULL) {
// abandoned lock -> red BG
clear_color[0] = 1.f;
}
int nrects;
pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
for (int i = 0; i < nrects; ++i) {
scissor_output(wlr_output, &rects[i]);
fx_renderer_clear(clear_color);
}
if (server.session_lock.lock != NULL) {
struct render_data data = {
.damage = damage,
.deco_data = get_undecorated_decoration_data(),
};
struct wlr_session_lock_surface_v1 *lock_surface;
wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
if (lock_surface->output != wlr_output) {
continue;
}
if (!lock_surface->mapped) {
continue;
}
output_surface_for_each_surface(output, lock_surface->surface,
0.0, 0.0, render_surface_iterator, &data);
}
}
goto renderer_end;
}
if (output_has_opaque_overlay_layer_surface(output)) {
goto render_overlay;
}
@ -1409,7 +1444,7 @@ renderer_end:
enum wl_output_transform transform =
wlr_output_transform_invert(wlr_output->transform);
wlr_region_transform(&frame_damage, &output->damage->current,
wlr_region_transform(&frame_damage, &output->damage_ring.current,
transform, width, height);
if (debug.damage != DAMAGE_DEFAULT) {
@ -1423,5 +1458,7 @@ renderer_end:
if (!wlr_output_commit(wlr_output)) {
return;
}
wlr_damage_ring_rotate(&output->damage_ring);
output->last_frame = *when;
}

View file

@ -1,7 +1,7 @@
#define _POSIX_C_SOURCE 200112L
#include <stdlib.h>
#include <time.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_compositor.h>
#include "sway/server.h"
#include "sway/surface.h"

View file

@ -24,11 +24,11 @@ static const struct sway_view_child_impl popup_impl;
static void popup_get_view_coords(struct sway_view_child *child,
int *sx, int *sy) {
struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
struct wlr_xdg_surface *surface = popup->wlr_xdg_surface;
struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
wlr_xdg_popup_get_toplevel_coords(surface->popup,
surface->popup->geometry.x - surface->current.geometry.x,
surface->popup->geometry.y - surface->current.geometry.y,
wlr_xdg_popup_get_toplevel_coords(wlr_popup,
wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x,
wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y,
sx, sy);
}
@ -65,7 +65,7 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
static void popup_unconstrain(struct sway_xdg_popup *popup) {
struct sway_view *view = popup->child.view;
struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
struct sway_output *output = view->container->pending.workspace->output;
@ -91,7 +91,7 @@ static struct sway_xdg_popup *popup_create(
return NULL;
}
view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
popup->wlr_xdg_surface = xdg_surface;
popup->wlr_xdg_popup = xdg_surface->popup;
wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
popup->new_popup.notify = popup_handle_new_popup;
@ -119,7 +119,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
static void get_constraints(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height) {
struct wlr_xdg_toplevel_state *state =
&view->wlr_xdg_surface->toplevel->current;
&view->wlr_xdg_toplevel->current;
*min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
*max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
*min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
@ -133,9 +133,9 @@ static const char *get_string_prop(struct sway_view *view,
}
switch (prop) {
case VIEW_PROP_TITLE:
return view->wlr_xdg_surface->toplevel->title;
return view->wlr_xdg_toplevel->title;
case VIEW_PROP_APP_ID:
return view->wlr_xdg_surface->toplevel->app_id;
return view->wlr_xdg_toplevel->app_id;
default:
return NULL;
}
@ -148,50 +148,45 @@ static uint32_t configure(struct sway_view *view, double lx, double ly,
if (xdg_shell_view == NULL) {
return 0;
}
return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height);
return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel,
width, height);
}
static void set_activated(struct sway_view *view, bool activated) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
wlr_xdg_toplevel_set_activated(surface, activated);
}
wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated);
}
static void set_tiled(struct sway_view *view, bool tiled) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
enum wlr_edges edges = WLR_EDGE_NONE;
if (tiled) {
edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
WLR_EDGE_BOTTOM;
}
wlr_xdg_toplevel_set_tiled(surface, edges);
wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
}
static void set_fullscreen(struct sway_view *view, bool fullscreen) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
wlr_xdg_toplevel_set_fullscreen(surface, fullscreen);
wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen);
}
static void set_resizing(struct sway_view *view, bool resizing) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
wlr_xdg_toplevel_set_resizing(surface, resizing);
wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing);
}
static bool wants_floating(struct sway_view *view) {
struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel;
struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
struct wlr_xdg_toplevel_state *state = &toplevel->current;
return (state->min_width != 0 && state->min_height != 0
&& (state->min_width == state->max_width
@ -204,7 +199,7 @@ static void for_each_surface(struct sway_view *view,
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator,
wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator,
user_data);
}
@ -213,8 +208,8 @@ static void for_each_popup_surface(struct sway_view *view,
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator,
user_data);
wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base,
iterator, user_data);
}
static bool is_transient_for(struct sway_view *child,
@ -222,12 +217,12 @@ static bool is_transient_for(struct sway_view *child,
if (xdg_shell_view_from_view(child) == NULL) {
return false;
}
struct wlr_xdg_surface *surface = child->wlr_xdg_surface;
while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
if (surface->toplevel->parent == ancestor->wlr_xdg_surface) {
struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel;
while (toplevel) {
if (toplevel->parent == ancestor->wlr_xdg_toplevel) {
return true;
}
surface = surface->toplevel->parent;
toplevel = toplevel->parent;
}
return false;
}
@ -236,17 +231,13 @@ static void _close(struct sway_view *view) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL
&& surface->toplevel) {
wlr_xdg_toplevel_send_close(surface);
}
wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel);
}
static void close_popups(struct sway_view *view) {
struct wlr_xdg_popup *popup, *tmp;
wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) {
wlr_xdg_popup_destroy(popup->base);
wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) {
wlr_xdg_popup_destroy(popup);
}
}
@ -280,7 +271,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, commit);
struct sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
struct wlr_box new_geo;
wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
@ -334,26 +325,27 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
popup_create(wlr_popup, &xdg_shell_view->view);
}
static void handle_request_maximize(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_maximize);
struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
wlr_xdg_surface_schedule_configure(toplevel->base);
}
static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_fullscreen);
struct wlr_xdg_toplevel_set_fullscreen_event *e = data;
struct wlr_xdg_surface *xdg_surface =
xdg_shell_view->view.wlr_xdg_surface;
struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
struct sway_view *view = &xdg_shell_view->view;
if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL,
"xdg_shell requested fullscreen of surface with role %i",
xdg_surface->role)) {
return;
}
if (!xdg_surface->mapped) {
if (!toplevel->base->mapped) {
return;
}
struct sway_container *container = view->container;
if (e->fullscreen && e->output && e->output->data) {
struct sway_output *output = e->output->data;
struct wlr_xdg_toplevel_requested *req = &toplevel->requested;
if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) {
struct sway_output *output = req->fullscreen_output->data;
struct sway_workspace *ws = output_get_active_workspace(output);
if (ws && !container_is_scratchpad_hidden(container) &&
container->pending.workspace != ws) {
@ -365,7 +357,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
}
}
container_set_fullscreen(container, e->fullscreen);
container_set_fullscreen(container, req->fullscreen);
arrange_root();
transaction_commit_dirty();
@ -375,7 +367,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_move);
struct sway_view *view = &xdg_shell_view->view;
if (!container_is_floating(view->container)) {
if (!container_is_floating(view->container) ||
view->container->pending.fullscreen_mode) {
return;
}
struct wlr_xdg_toplevel_move_event *e = data;
@ -412,6 +405,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_view->commit.link);
wl_list_remove(&xdg_shell_view->new_popup.link);
wl_list_remove(&xdg_shell_view->request_maximize.link);
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
wl_list_remove(&xdg_shell_view->request_move.link);
wl_list_remove(&xdg_shell_view->request_resize.link);
@ -423,13 +417,13 @@ static void handle_map(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, map);
struct sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
view->natural_width = view->wlr_xdg_surface->current.geometry.width;
view->natural_height = view->wlr_xdg_surface->current.geometry.height;
view->natural_width = toplevel->base->current.geometry.width;
view->natural_height = toplevel->base->current.geometry.height;
if (!view->natural_width && !view->natural_height) {
view->natural_width = view->wlr_xdg_surface->surface->current.width;
view->natural_height = view->wlr_xdg_surface->surface->current.height;
view->natural_width = toplevel->base->surface->current.width;
view->natural_height = toplevel->base->surface->current.height;
}
bool csd = false;
@ -440,44 +434,48 @@ static void handle_map(struct wl_listener *listener, void *data) {
csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
} else {
struct sway_server_decoration *deco =
decoration_from_surface(xdg_surface->surface);
decoration_from_surface(toplevel->base->surface);
csd = !deco || deco->wlr_server_decoration->mode ==
WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
}
view_map(view, view->wlr_xdg_surface->surface,
xdg_surface->toplevel->requested.fullscreen,
xdg_surface->toplevel->requested.fullscreen_output,
view_map(view, toplevel->base->surface,
toplevel->requested.fullscreen,
toplevel->requested.fullscreen_output,
csd);
transaction_commit_dirty();
xdg_shell_view->commit.notify = handle_commit;
wl_signal_add(&xdg_surface->surface->events.commit,
wl_signal_add(&toplevel->base->surface->events.commit,
&xdg_shell_view->commit);
xdg_shell_view->new_popup.notify = handle_new_popup;
wl_signal_add(&xdg_surface->events.new_popup,
wl_signal_add(&toplevel->base->events.new_popup,
&xdg_shell_view->new_popup);
xdg_shell_view->request_maximize.notify = handle_request_maximize;
wl_signal_add(&toplevel->events.request_maximize,
&xdg_shell_view->request_maximize);
xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
wl_signal_add(&toplevel->events.request_fullscreen,
&xdg_shell_view->request_fullscreen);
xdg_shell_view->request_move.notify = handle_request_move;
wl_signal_add(&xdg_surface->toplevel->events.request_move,
wl_signal_add(&toplevel->events.request_move,
&xdg_shell_view->request_move);
xdg_shell_view->request_resize.notify = handle_request_resize;
wl_signal_add(&xdg_surface->toplevel->events.request_resize,
wl_signal_add(&toplevel->events.request_resize,
&xdg_shell_view->request_resize);
xdg_shell_view->set_title.notify = handle_set_title;
wl_signal_add(&xdg_surface->toplevel->events.set_title,
wl_signal_add(&toplevel->events.set_title,
&xdg_shell_view->set_title);
xdg_shell_view->set_app_id.notify = handle_set_app_id;
wl_signal_add(&xdg_surface->toplevel->events.set_app_id,
wl_signal_add(&toplevel->events.set_app_id,
&xdg_shell_view->set_app_id);
}
@ -491,7 +489,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_view->destroy.link);
wl_list_remove(&xdg_shell_view->map.link);
wl_list_remove(&xdg_shell_view->unmap.link);
view->wlr_xdg_surface = NULL;
view->wlr_xdg_toplevel = NULL;
if (view->xdg_decoration) {
view->xdg_decoration->view = NULL;
}
@ -522,7 +520,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
}
view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
xdg_shell_view->view.wlr_xdg_surface = xdg_surface;
xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel;
xdg_shell_view->map.notify = handle_map;
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);

View file

@ -5,7 +5,9 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/xwayland.h>
#include <xcb/xcb_icccm.h>
#include "log.h"
#include "sway/desktop.h"
#include "sway/desktop/transaction.h"
@ -15,6 +17,7 @@
#include "sway/output.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/server.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
@ -121,6 +124,20 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
}
}
static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
struct wlr_xwayland_surface *xsurface = data;
if (!xsurface->mapped) {
return;
}
struct sway_seat *seat = input_manager_current_seat();
struct sway_container *focus = seat_get_focused_container(seat);
if (focus && focus->view && focus->view->pid != xsurface->pid) {
return;
}
seat_set_focus_surface(seat, xsurface->surface, false);
}
static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_xwayland_unmanaged *surface =
wl_container_of(listener, surface, destroy);
@ -129,6 +146,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&surface->unmap.link);
wl_list_remove(&surface->destroy.link);
wl_list_remove(&surface->override_redirect.link);
wl_list_remove(&surface->request_activate.link);
free(surface);
}
@ -176,6 +194,8 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
surface->destroy.notify = unmanaged_handle_destroy;
wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
surface->override_redirect.notify = unmanaged_handle_override_redirect;
wl_signal_add(&xsurface->events.request_activate, &surface->request_activate);
surface->request_activate.notify = unmanaged_handle_request_activate;
return surface;
}
@ -294,7 +314,7 @@ static bool wants_floating(struct sway_view *view) {
}
}
struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints;
xcb_size_hints_t *size_hints = surface->size_hints;
if (size_hints != NULL &&
size_hints->min_width > 0 && size_hints->min_height > 0 &&
(size_hints->max_width == size_hints->min_width ||
@ -348,7 +368,7 @@ static void destroy(struct sway_view *view) {
static void get_constraints(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height) {
struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints;
xcb_size_hints_t *size_hints = surface->size_hints;
if (size_hints == NULL) {
*min_width = DBL_MIN;
@ -448,6 +468,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&xwayland_view->set_title.link);
wl_list_remove(&xwayland_view->set_class.link);
wl_list_remove(&xwayland_view->set_role.link);
wl_list_remove(&xwayland_view->set_startup_id.link);
wl_list_remove(&xwayland_view->set_window_type.link);
wl_list_remove(&xwayland_view->set_hints.link);
wl_list_remove(&xwayland_view->set_decorations.link);
@ -577,7 +598,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
if (!xsurface->mapped) {
return;
}
if (!container_is_floating(view->container)) {
if (!container_is_floating(view->container) ||
view->container->pending.fullscreen_mode) {
return;
}
struct sway_seat *seat = input_manager_current_seat();
@ -647,6 +669,31 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
view_execute_criteria(view);
}
static void handle_set_startup_id(struct wl_listener *listener, void *data) {
struct sway_xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, set_startup_id);
struct sway_view *view = &xwayland_view->view;
struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
if (xsurface->startup_id == NULL) {
return;
}
struct wlr_xdg_activation_token_v1 *token =
wlr_xdg_activation_v1_find_token(
server.xdg_activation_v1, xsurface->startup_id);
if (token == NULL) {
// Tried to activate with an unknown or expired token
return;
}
struct launcher_ctx *ctx = token->data;
if (token->data == NULL) {
// TODO: support external launchers in X
return;
}
view_assign_ctx(view, ctx);
}
static void handle_set_window_type(struct wl_listener *listener, void *data) {
struct sway_xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, set_window_type);
@ -666,14 +713,15 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
if (!xsurface->mapped) {
return;
}
if (!xsurface->hints_urgency && view->urgent_timer) {
const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
if (!hints_urgency && view->urgent_timer) {
// The view is in the timeout period. We'll ignore the request to
// unset urgency so that the view remains urgent until the timer clears
// it.
return;
}
if (view->allow_request_urgent) {
view_set_urgent(view, (bool)xsurface->hints_urgency);
view_set_urgent(view, hints_urgency);
}
}
@ -731,6 +779,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
xwayland_view->set_role.notify = handle_set_role;
wl_signal_add(&xsurface->events.set_startup_id,
&xwayland_view->set_startup_id);
xwayland_view->set_startup_id.notify = handle_set_startup_id;
wl_signal_add(&xsurface->events.set_window_type,
&xwayland_view->set_window_type);
xwayland_view->set_window_type.notify = handle_set_window_type;

View file

@ -386,28 +386,29 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
static void handle_pointer_motion_relative(
struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
struct wlr_event_pointer_motion *e = data;
cursor_handle_activity_from_device(cursor, e->device);
struct wlr_pointer_motion_event *e = data;
cursor_handle_activity_from_device(cursor, &e->pointer->base);
pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y,
e->unaccel_dx, e->unaccel_dy);
pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x,
e->delta_y, e->unaccel_dx, e->unaccel_dy);
}
static void handle_pointer_motion_absolute(
struct wl_listener *listener, void *data) {
struct sway_cursor *cursor =
wl_container_of(listener, cursor, motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_pointer_motion_absolute_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
double lx, ly;
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base,
event->x, event->y, &lx, &ly);
double dx = lx - cursor->cursor->x;
double dy = ly - cursor->cursor->y;
pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy,
dx, dy);
}
void dispatch_cursor_button(struct sway_cursor *cursor,
@ -422,7 +423,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
static void handle_pointer_button(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
struct wlr_event_pointer_button *event = data;
struct wlr_pointer_button_event *event = data;
if (event->state == WLR_BUTTON_PRESSED) {
cursor->pressed_button_count++;
@ -434,20 +435,20 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
}
}
cursor_handle_activity_from_device(cursor, event->device);
dispatch_cursor_button(cursor, event->device,
cursor_handle_activity_from_device(cursor, &event->pointer->base);
dispatch_cursor_button(cursor, &event->pointer->base,
event->time_msec, event->button, event->state);
}
void dispatch_cursor_axis(struct sway_cursor *cursor,
struct wlr_event_pointer_axis *event) {
struct wlr_pointer_axis_event *event) {
seatop_pointer_axis(cursor->seat, event);
}
static void handle_pointer_axis(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
struct wlr_event_pointer_axis *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_pointer_axis_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
dispatch_cursor_axis(cursor, event);
}
@ -458,8 +459,8 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) {
static void handle_touch_down(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
struct wlr_event_touch_down *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_touch_down_event *event = data;
cursor_handle_activity_from_device(cursor, &event->touch->base);
cursor_hide(cursor);
struct sway_seat *seat = cursor->seat;
@ -467,7 +468,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
struct wlr_surface *surface = NULL;
double lx, ly;
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
event->x, event->y, &lx, &ly);
double sx, sy;
struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy);
@ -495,24 +496,25 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
double dx, dy;
dx = lx - cursor->cursor->x;
dy = ly - cursor->cursor->y;
pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
dispatch_cursor_button(cursor, event->device, event->time_msec,
pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
dx, dy);
dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
BTN_LEFT, WLR_BUTTON_PRESSED);
}
}
static void handle_touch_up(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
struct wlr_event_touch_up *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_touch_up_event *event = data;
cursor_handle_activity_from_device(cursor, &event->touch->base);
struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
if (cursor->simulating_pointer_from_touch) {
if (cursor->pointer_touch_id == cursor->seat->touch_id) {
cursor->pointer_touch_up = true;
dispatch_cursor_button(cursor, event->device, event->time_msec,
BTN_LEFT, WLR_BUTTON_RELEASED);
dispatch_cursor_button(cursor, &event->touch->base,
event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED);
}
} else {
wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id);
@ -522,15 +524,15 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
static void handle_touch_motion(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor =
wl_container_of(listener, cursor, touch_motion);
struct wlr_event_touch_motion *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_touch_motion_event *event = data;
cursor_handle_activity_from_device(cursor, &event->touch->base);
struct sway_seat *seat = cursor->seat;
struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_surface *surface = NULL;
double lx, ly;
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
event->x, event->y, &lx, &ly);
double sx, sy;
node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
@ -552,7 +554,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
double dx, dy;
dx = lx - cursor->cursor->x;
dy = ly - cursor->cursor->y;
pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
pointer_motion(cursor, event->time_msec, &event->touch->base,
dx, dy, dx, dy);
}
} else if (surface) {
wlr_seat_touch_notify_motion(wlr_seat, event->time_msec,
@ -591,14 +594,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
double x1 = region->x1, x2 = region->x2;
double y1 = region->y1, y2 = region->y2;
if (region->mm) {
if (device->width_mm == 0 || device->height_mm == 0) {
if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
if (tablet->width_mm == 0 || tablet->height_mm == 0) {
return;
}
x1 /= device->width_mm;
x2 /= device->width_mm;
y1 /= device->height_mm;
y2 /= device->height_mm;
x1 /= tablet->width_mm;
x2 /= tablet->width_mm;
y1 /= tablet->height_mm;
y2 /= tablet->height_mm;
}
*x = apply_mapping_from_coord(x1, x2, *x);
@ -660,8 +664,8 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
static void handle_tool_axis(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
struct wlr_event_tablet_tool_axis *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_tablet_tool_axis_event *event = data;
cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct sway_tablet_tool *sway_tool = event->tool->data;
if (!sway_tool) {
@ -716,8 +720,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
struct wlr_event_tablet_tool_tip *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_tablet_tool_tip_event *event = data;
cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct sway_tablet_tool *sway_tool = event->tool->data;
struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2;
@ -732,7 +736,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
if (cursor->simulating_pointer_from_tool_tip &&
event->state == WLR_TABLET_TOOL_TIP_UP) {
cursor->simulating_pointer_from_tool_tip = false;
dispatch_cursor_button(cursor, event->device, event->time_msec,
dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
BTN_LEFT, WLR_BUTTON_RELEASED);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
} else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
@ -744,8 +748,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
WLR_TABLET_TOOL_TIP_UP);
} else {
cursor->simulating_pointer_from_tool_tip = true;
dispatch_cursor_button(cursor, event->device, event->time_msec,
BTN_LEFT, WLR_BUTTON_PRESSED);
dispatch_cursor_button(cursor, &event->tablet->base,
event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
}
} else {
@ -767,12 +771,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor,
static void handle_tool_proximity(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor =
wl_container_of(listener, cursor, tool_proximity);
struct wlr_event_tablet_tool_proximity *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_tablet_tool_proximity_event *event = data;
cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct wlr_tablet_tool *tool = event->tool;
if (!tool->data) {
struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device);
struct sway_tablet *tablet = get_tablet_for_device(cursor,
&event->tablet->base);
if (!tablet) {
sway_log(SWAY_ERROR, "no tablet for tablet tool");
return;
@ -797,8 +802,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) {
static void handle_tool_button(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
struct wlr_event_tablet_tool_button *event = data;
cursor_handle_activity_from_device(cursor, event->device);
struct wlr_tablet_tool_button_event *event = data;
cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct sway_tablet_tool *sway_tool = event->tool->data;
if (!sway_tool) {
@ -819,14 +824,14 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
switch (event->state) {
case WLR_BUTTON_PRESSED:
if (cursor->tool_buttons == 0) {
dispatch_cursor_button(cursor, event->device,
dispatch_cursor_button(cursor, &event->tablet->base,
event->time_msec, BTN_RIGHT, event->state);
}
cursor->tool_buttons++;
break;
case WLR_BUTTON_RELEASED:
if (cursor->tool_buttons == 1) {
dispatch_cursor_button(cursor, event->device,
dispatch_cursor_button(cursor, &event->tablet->base,
event->time_msec, BTN_RIGHT, event->state);
}
cursor->tool_buttons--;
@ -923,65 +928,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
event->hotspot_y, focused_client);
}
static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, hold_begin);
struct wlr_pointer_hold_begin_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_hold_begin(cursor->seat, event);
}
static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, hold_end);
struct wlr_pointer_hold_end_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_hold_end(cursor->seat, event);
}
static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_begin);
struct wlr_event_pointer_pinch_begin *event = data;
cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_pinch_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
struct wlr_pointer_pinch_begin_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_pinch_begin(cursor->seat, event);
}
static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_update);
struct wlr_event_pointer_pinch_update *event = data;
cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_pinch_update(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy,
event->scale, event->rotation);
struct wlr_pointer_pinch_update_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_pinch_update(cursor->seat, event);
}
static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_end);
struct wlr_event_pointer_pinch_end *event = data;
cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_pinch_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
struct wlr_pointer_pinch_end_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_pinch_end(cursor->seat, event);
}
static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_begin);
struct wlr_event_pointer_swipe_begin *event = data;
cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_swipe_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
struct wlr_pointer_swipe_begin_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_swipe_begin(cursor->seat, event);
}
static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_update);
struct wlr_event_pointer_swipe_update *event = data;
cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_swipe_update(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy);
struct wlr_pointer_swipe_update_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_swipe_update(cursor->seat, event);
}
static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_end);
struct wlr_event_pointer_swipe_end *event = data;
cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_swipe_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
struct wlr_pointer_swipe_end_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_swipe_end(cursor->seat, event);
}
static void handle_image_surface_destroy(struct wl_listener *listener,
@ -1055,6 +1063,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
wl_event_source_remove(cursor->hide_source);
wl_list_remove(&cursor->image_surface_destroy.link);
wl_list_remove(&cursor->hold_begin.link);
wl_list_remove(&cursor->hold_end.link);
wl_list_remove(&cursor->pinch_begin.link);
wl_list_remove(&cursor->pinch_update.link);
wl_list_remove(&cursor->pinch_end.link);
@ -1104,19 +1114,27 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
wl_list_init(&cursor->image_surface_destroy.link);
cursor->image_surface_destroy.notify = handle_image_surface_destroy;
// gesture events
cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display);
cursor->pinch_begin.notify = handle_pointer_pinch_begin;
wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
cursor->hold_begin.notify = handle_pointer_hold_begin;
wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
cursor->hold_end.notify = handle_pointer_hold_end;
wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
cursor->pinch_update.notify = handle_pointer_pinch_update;
cursor->pinch_begin.notify = handle_pointer_pinch_begin;
wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
cursor->pinch_end.notify = handle_pointer_pinch_end;
cursor->pinch_update.notify = handle_pointer_pinch_update;
wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
cursor->swipe_begin.notify = handle_pointer_swipe_begin;
cursor->pinch_end.notify = handle_pointer_pinch_end;
wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
cursor->swipe_update.notify = handle_pointer_swipe_update;
cursor->swipe_begin.notify = handle_pointer_swipe_begin;
wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
cursor->swipe_end.notify = handle_pointer_swipe_end;
cursor->swipe_update.notify = handle_pointer_swipe_update;
wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
cursor->swipe_end.notify = handle_pointer_swipe_end;
// input events
wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
@ -1354,14 +1372,11 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
sway_constraint->destroy.notify = handle_constraint_destroy;
wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
struct sway_node *focus = seat_get_focus(seat);
if (focus && node_is_view(focus)) {
struct wlr_surface *surface = focus->sway_container->view->surface;
if (surface == constraint->surface) {
struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
if (surface && surface == constraint->surface) {
sway_cursor_constrain(seat->cursor, constraint);
}
}
}
void sway_cursor_constrain(struct sway_cursor *cursor,
struct wlr_pointer_constraint_v1 *constraint) {

View file

@ -236,7 +236,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
apply_input_type_config(input_device);
sway_input_configure_libinput_device(input_device);
bool config_changed = sway_input_configure_libinput_device(input_device);
wl_signal_add(&device->events.destroy, &input_device->device_destroy);
input_device->device_destroy.notify = handle_device_destroy;
@ -274,6 +274,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
}
ipc_event_input("added", input_device);
if (config_changed) {
ipc_event_input("libinput_config", input_device);
}
}
static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
@ -289,6 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
struct sway_input_manager *input_manager = wl_container_of(
listener, input_manager, inhibit_deactivate);
struct sway_seat *seat;
if (server.session_lock.locked) {
// Don't deactivate the grab of a screenlocker
return;
}
wl_list_for_each(seat, &input_manager->seats, link) {
seat_set_exclusive_client(seat, NULL);
struct sway_node *previous = seat_get_focus(seat);
@ -377,7 +385,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
struct sway_input_manager *input_manager =
wl_container_of(listener, input_manager, virtual_keyboard_new);
struct wlr_virtual_keyboard_v1 *keyboard = data;
struct wlr_input_device *device = &keyboard->input_device;
struct wlr_input_device *device = &keyboard->keyboard.base;
// TODO: Amend protocol to allow NULL seat
struct sway_seat *seat = keyboard->seat ?
@ -410,7 +418,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
wl_container_of(listener, input_manager, virtual_pointer_new);
struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
struct wlr_input_device *device = &pointer->input_device;
struct wlr_input_device *device = &pointer->pointer.base;
struct sway_seat *seat = event->suggested_seat ?
input_manager_sway_seat_from_wlr_seat(event->suggested_seat) :
@ -524,11 +532,14 @@ static void retranslate_keysyms(struct input_config *input_config) {
static void input_manager_configure_input(
struct sway_input_device *input_device) {
sway_input_configure_libinput_device(input_device);
bool config_changed = sway_input_configure_libinput_device(input_device);
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &server.input->seats, link) {
seat_configure_device(seat, input_device);
}
if (config_changed) {
ipc_event_input("libinput_config", input_device);
}
}
void input_manager_configure_all_inputs(void) {
@ -564,6 +575,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) {
}
void input_manager_reset_all_inputs(void) {
// Set the active keyboard to NULL to avoid spamming configuration updates
// for all keyboard devices.
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
wlr_seat_set_keyboard(seat->wlr_seat, NULL);
}
struct sway_input_device *input_device = NULL;
wl_list_for_each(input_device, &server.input->devices, link) {
input_manager_reset_input(input_device);
@ -572,7 +590,6 @@ void input_manager_reset_all_inputs(void) {
// If there is at least one keyboard using the default keymap, repeat delay,
// and repeat rate, then it is possible that there is a keyboard group that
// need their keyboard disarmed.
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
struct sway_keyboard_group *group;
wl_list_for_each(group, &seat->keyboard_groups, link) {

View file

@ -291,14 +291,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
uint32_t *modifiers) {
struct wlr_input_device *device =
keyboard->seat_device->input_device->wlr_device;
*modifiers = wlr_keyboard_get_modifiers(device->keyboard);
*modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
*modifiers = *modifiers & ~consumed;
return xkb_state_key_get_syms(device->keyboard->xkb_state,
return xkb_state_key_get_syms(keyboard->wlr->xkb_state,
keycode, keysyms);
}
@ -314,13 +312,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
uint32_t *modifiers) {
struct wlr_input_device *device =
keyboard->seat_device->input_device->wlr_device;
*modifiers = wlr_keyboard_get_modifiers(device->keyboard);
*modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
xkb_layout_index_t layout_index = xkb_state_key_get_layout(
device->keyboard->xkb_state, keycode);
return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap,
keyboard->wlr->xkb_state, keycode);
return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap,
keycode, layout_index, 0, keysyms);
}
@ -360,8 +356,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
keyinfo->keycode, &keyinfo->translated_keysyms,
&keyinfo->translated_modifiers);
keyinfo->code_modifiers = wlr_keyboard_get_modifiers(
keyboard->seat_device->input_device->wlr_device->keyboard);
keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
// Update shortcut model keyinfo
update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate,
@ -401,15 +396,16 @@ static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(
}
static void handle_key_event(struct sway_keyboard *keyboard,
struct wlr_event_keyboard_key *event) {
struct wlr_keyboard_key_event *event) {
struct sway_seat *seat = keyboard->seat_device->sway_seat;
struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device;
char *device_identifier = input_device_get_identifier(wlr_device);
bool exact_identifier = wlr_device->keyboard->group != NULL;
bool exact_identifier = keyboard->wlr->group != NULL;
seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
bool input_inhibited = seat->exclusive_client != NULL;
bool input_inhibited = seat->exclusive_client != NULL ||
server.session_lock.locked;
struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@ -477,10 +473,10 @@ static void handle_key_event(struct sway_keyboard *keyboard,
// Set up (or clear) keyboard repeat for a pressed binding. Since the
// binding may remove the keyboard, the timer needs to be updated first
if (binding && !(binding->flags & BINDING_NOREPEAT) &&
wlr_device->keyboard->repeat_info.delay > 0) {
keyboard->wlr->repeat_info.delay > 0) {
keyboard->repeat_binding = binding;
if (wl_event_source_timer_update(keyboard->key_repeat_source,
wlr_device->keyboard->repeat_info.delay) < 0) {
keyboard->wlr->repeat_info.delay) < 0) {
sway_log(SWAY_DEBUG, "failed to set key repeat timer");
}
} else if (keyboard->repeat_binding) {
@ -492,7 +488,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
handled = true;
}
if (!handled && wlr_device->keyboard->group) {
if (!handled && keyboard->wlr->group) {
// Only handle device specific bindings for keyboards in a group
free(device_identifier);
return;
@ -517,7 +513,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
&keyboard->state_pressed_sent, event->keycode,
event->state, keyinfo.keycode, 0);
if (pressed_sent) {
wlr_seat_set_keyboard(wlr_seat, wlr_device);
wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
event->keycode, event->state);
handled = true;
@ -528,8 +524,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
if (kb_grab) {
wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab,
wlr_device->keyboard);
wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
event->time_msec, event->keycode, event->state);
handled = true;
@ -542,7 +537,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
update_shortcut_state(
&keyboard->state_pressed_sent, event->keycode, event->state,
keyinfo.keycode, 0);
wlr_seat_set_keyboard(wlr_seat, wlr_device);
wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
event->keycode, event->state);
}
@ -618,14 +613,12 @@ static void handle_keyboard_group_leave(struct wl_listener *listener,
}
static int handle_keyboard_repeat(void *data) {
struct sway_keyboard *keyboard = (struct sway_keyboard *)data;
struct wlr_keyboard *wlr_device =
keyboard->seat_device->input_device->wlr_device->keyboard;
struct sway_keyboard *keyboard = data;
if (keyboard->repeat_binding) {
if (wlr_device->repeat_info.rate > 0) {
if (keyboard->wlr->repeat_info.rate > 0) {
// We queue the next event first, as the command might cancel it
if (wl_event_source_timer_update(keyboard->key_repeat_source,
1000 / wlr_device->repeat_info.rate) < 0) {
1000 / keyboard->wlr->repeat_info.rate) < 0) {
sway_log(SWAY_DEBUG, "failed to update key repeat timer");
}
}
@ -658,31 +651,28 @@ static void determine_bar_visibility(uint32_t modifiers) {
}
static void handle_modifier_event(struct sway_keyboard *keyboard) {
struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device;
if (!wlr_device->keyboard->group) {
if (!keyboard->wlr->group) {
struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
if (kb_grab) {
wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab,
wlr_device->keyboard);
wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
&wlr_device->keyboard->modifiers);
&keyboard->wlr->modifiers);
} else {
struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
wlr_seat_set_keyboard(wlr_seat, wlr_device);
wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
wlr_seat_keyboard_notify_modifiers(wlr_seat,
&wlr_device->keyboard->modifiers);
&keyboard->wlr->modifiers);
}
uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard);
uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
determine_bar_visibility(modifiers);
}
if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) {
keyboard->effective_layout = wlr_device->keyboard->modifiers.group;
if (keyboard->wlr->modifiers.group != keyboard->effective_layout) {
keyboard->effective_layout = keyboard->wlr->modifiers.group;
if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) {
if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) {
ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
}
}
@ -711,6 +701,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
}
keyboard->seat_device = device;
keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device);
device->keyboard = keyboard;
wl_list_init(&keyboard->keyboard_key.link);
@ -819,13 +810,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) {
static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
struct sway_input_device *device = keyboard->seat_device->input_device;
struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
struct wlr_keyboard_group *wlr_group = keyboard->wlr->group;
sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p",
device->identifier, wlr_group);
wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard);
wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr);
if (wl_list_empty(&wlr_group->devices)) {
sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p",
@ -850,9 +840,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
}
static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
struct sway_input_device *device = keyboard->seat_device->input_device;
struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
if (!wlr_keyboard->group) {
if (!keyboard->wlr->group) {
return;
}
@ -868,7 +856,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
break;
case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
case KEYBOARD_GROUP_SMART:;
struct wlr_keyboard_group *group = wlr_keyboard->group;
struct wlr_keyboard_group *group = keyboard->wlr->group;
if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) ||
!repeat_info_match(keyboard, &group->keyboard)) {
sway_keyboard_group_remove(keyboard);
@ -879,7 +867,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
struct sway_input_device *device = keyboard->seat_device->input_device;
struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
struct sway_seat *seat = keyboard->seat_device->sway_seat;
struct seat_config *sc = seat_get_config(seat);
@ -911,7 +898,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
repeat_info_match(keyboard, &wlr_group->keyboard)) {
sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
device->identifier, wlr_group);
wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard);
wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr);
return;
}
break;
@ -950,7 +937,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
goto cleanup;
}
sway_group->seat_device->input_device->wlr_device =
sway_group->wlr_group->input_device;
&sway_group->wlr_group->keyboard.base;
if (!sway_keyboard_create(seat, sway_group->seat_device)) {
sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group");
@ -959,7 +946,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
device->identifier, sway_group->wlr_group);
wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard);
wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr);
wl_list_insert(&seat->keyboard_groups, &sway_group->link);
@ -991,10 +978,8 @@ cleanup:
void sway_keyboard_configure(struct sway_keyboard *keyboard) {
struct input_config *input_config =
input_device_get_config(keyboard->seat_device->input_device);
struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device;
if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard),
if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr),
"sway_keyboard_configure should not be called with a "
"keyboard group's keyboard")) {
return;
@ -1036,11 +1021,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
sway_keyboard_group_remove_invalid(keyboard);
wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
wlr_keyboard_set_repeat_info(wlr_device->keyboard,
wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap);
wlr_keyboard_set_repeat_info(keyboard->wlr,
keyboard->repeat_rate, keyboard->repeat_delay);
if (!wlr_device->keyboard->group) {
if (!keyboard->wlr->group) {
sway_keyboard_group_add(keyboard);
}
@ -1060,40 +1045,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
}
}
if (locked_mods) {
wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0,
wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0,
locked_mods, 0);
uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(
wlr_device->keyboard->xkb_state,
wlr_device->keyboard->led_indexes[i])) {
if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state,
keyboard->wlr->led_indexes[i])) {
leds |= (1 << i);
}
}
if (wlr_device->keyboard->group) {
wlr_keyboard_led_update(
&wlr_device->keyboard->group->keyboard, leds);
if (keyboard->wlr->group) {
wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds);
} else {
wlr_keyboard_led_update(wlr_device->keyboard, leds);
wlr_keyboard_led_update(keyboard->wlr, leds);
}
}
} else {
xkb_keymap_unref(keymap);
sway_keyboard_group_remove_invalid(keyboard);
if (!wlr_device->keyboard->group) {
if (!keyboard->wlr->group) {
sway_keyboard_group_add(keyboard);
}
}
// If the seat has no active keyboard, set this one
struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
wlr_seat_set_keyboard(seat, wlr_device);
struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
if (current_keyboard == NULL) {
wlr_seat_set_keyboard(seat, keyboard->wlr);
}
wl_list_remove(&keyboard->keyboard_key.link);
wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);
wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
keyboard->keyboard_key.notify = handle_keyboard_key;
wl_list_remove(&keyboard->keyboard_modifiers.link);
wl_signal_add(&wlr_device->keyboard->events.modifiers,
wl_signal_add(&keyboard->wlr->events.modifiers,
&keyboard->keyboard_modifiers);
keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
@ -1110,12 +1097,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
if (!keyboard) {
return;
}
if (keyboard->seat_device->input_device->wlr_device->keyboard->group) {
if (keyboard->wlr->group) {
sway_keyboard_group_remove(keyboard);
}
struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
struct sway_input_device *device = keyboard->seat_device->input_device;
if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) {
if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) {
wlr_seat_set_keyboard(wlr_seat, NULL);
}
if (keyboard->keymap) {

View file

@ -166,6 +166,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) {
return true;
}
static bool set_dwtp(struct libinput_device *device, bool dwtp) {
if (!libinput_device_config_dwtp_is_available(device) ||
libinput_device_config_dwtp_get_enabled(device) == dwtp) {
return false;
}
sway_log(SWAY_DEBUG, "dwtp_set_enabled(%d)", dwtp);
log_status(libinput_device_config_dwtp_set_enabled(device, dwtp));
return true;
}
static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
if (!libinput_device_config_calibration_has_matrix(dev)) {
return false;
@ -187,10 +197,10 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
return changed;
}
void sway_input_configure_libinput_device(struct sway_input_device *input_device) {
bool sway_input_configure_libinput_device(struct sway_input_device *input_device) {
struct input_config *ic = input_device_get_config(input_device);
if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
return;
return false;
}
struct libinput_device *device =
@ -255,13 +265,14 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
if (ic->dwt != INT_MIN) {
changed |= set_dwt(device, ic->dwt);
}
if (ic->dwtp != INT_MIN) {
changed |= set_dwtp(device, ic->dwtp);
}
if (ic->calibration_matrix.configured) {
changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix);
}
if (changed) {
ipc_event_input("libinput_config", input_device);
}
return changed;
}
void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
@ -304,6 +315,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
libinput_device_config_scroll_get_default_button(device));
changed |= set_dwt(device,
libinput_device_config_dwt_get_default_enabled(device));
changed |= set_dwtp(device,
libinput_device_config_dwtp_get_default_enabled(device));
float matrix[6];
libinput_device_config_calibration_get_default_matrix(device, matrix);

View file

@ -7,10 +7,12 @@
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_keyboard_group.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include "config.h"
#include "list.h"
@ -42,6 +44,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
sway_keyboard_destroy(seat_device->keyboard);
sway_tablet_destroy(seat_device->tablet);
sway_tablet_pad_destroy(seat_device->tablet_pad);
sway_switch_destroy(seat_device->switch_device);
wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
seat_device->input_device->wlr_device);
wl_list_remove(&seat_device->link);
@ -110,6 +113,7 @@ void seat_idle_notify_activity(struct sway_seat *seat,
}
if ((source & mask) > 0) {
wlr_idle_notify_activity(server.idle, seat->wlr_seat);
wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat);
}
}
@ -140,7 +144,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
continue;
}
if (input_device->wlr_device->keyboard == wlr_keyboard) {
if (input_device->wlr_device == &wlr_keyboard->base) {
return seat_device->keyboard;
}
}
@ -148,7 +152,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
wl_list_for_each(group, &seat->keyboard_groups, link) {
struct sway_input_device *input_device =
group->seat_device->input_device;
if (input_device->wlr_device->keyboard == wlr_keyboard) {
if (input_device->wlr_device == &wlr_keyboard->base) {
return group->seat_device->keyboard;
}
}
@ -245,7 +249,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
seat_node_destroy(seat_node);
// If an unmanaged or layer surface is focused when an output gets
// disabled and an empty workspace on the output was focused by the
// seat, the seat needs to refocus it's focus inactive to update the
// seat, the seat needs to refocus its focus inactive to update the
// value of seat->workspace.
if (seat->workspace == node->sway_workspace) {
struct sway_node *node = seat_get_focus_inactive(seat, &root->node);
@ -378,8 +382,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
case WLR_DRAG_GRAB_KEYBOARD:
return;
case WLR_DRAG_GRAB_KEYBOARD_POINTER:
icon->x = cursor->x;
icon->y = cursor->y;
icon->x = cursor->x + wlr_icon->surface->sx;
icon->y = cursor->y + wlr_icon->surface->sy;
break;
case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
struct wlr_touch_point *point =
@ -387,8 +391,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
if (point == NULL) {
return;
}
icon->x = seat->touch_x;
icon->y = seat->touch_y;
icon->x = seat->touch_x + wlr_icon->surface->sx;
icon->y = seat->touch_y + wlr_icon->surface->sy;
}
drag_icon_damage_whole(icon);
@ -724,14 +728,25 @@ static void seat_apply_input_config(struct sway_seat *seat,
ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to;
switch (mapped_to) {
case MAPPED_TO_DEFAULT:
case MAPPED_TO_DEFAULT:;
/*
* If the wlroots backend provides an output name, use that.
*
* Otherwise, try to map built-in touch and tablet tool devices to the
* Otherwise, try to map built-in touch and pointer devices to the
* built-in output.
*/
mapped_to_output = sway_device->input_device->wlr_device->output_name;
struct wlr_input_device *dev = sway_device->input_device->wlr_device;
switch (dev->type) {
case WLR_INPUT_DEVICE_POINTER:
mapped_to_output = wlr_pointer_from_input_device(dev)->output_name;
break;
case WLR_INPUT_DEVICE_TOUCH:
mapped_to_output = wlr_touch_from_input_device(dev)->output_name;
break;
default:
mapped_to_output = NULL;
break;
}
if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
sway_libinput_device_is_builtin(sway_device->input_device)) {
mapped_to_output = get_builtin_output_name();
@ -799,13 +814,22 @@ static void seat_configure_keyboard(struct sway_seat *seat,
sway_keyboard_create(seat, seat_device);
}
sway_keyboard_configure(seat_device->keyboard);
wlr_seat_set_keyboard(seat->wlr_seat,
seat_device->input_device->wlr_device);
struct sway_node *focus = seat_get_focus(seat);
if (focus && node_is_view(focus)) {
// force notify reenter to pick up the new configuration
// We only need to update the current keyboard, as the rest will be updated
// as they are activated.
struct wlr_keyboard *wlr_keyboard =
wlr_keyboard_from_input_device(seat_device->input_device->wlr_device);
struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard;
if (wlr_keyboard != current_keyboard) {
return;
}
// force notify reenter to pick up the new configuration. This reuses
// the current focused surface to avoid breaking input grabs.
struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
if (surface) {
wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
seat_keyboard_notify_enter(seat, focus->sway_container->view->surface);
seat_keyboard_notify_enter(seat, surface);
}
}
@ -1057,7 +1081,8 @@ void seat_configure_xcursor(struct sway_seat *seat) {
bool seat_is_input_allowed(struct sway_seat *seat,
struct wlr_surface *surface) {
struct wl_client *client = wl_resource_get_client(surface->resource);
return !seat->exclusive_client || seat->exclusive_client == client;
return seat->exclusive_client == client ||
(seat->exclusive_client == NULL && !server.session_lock.locked);
}
static void send_unfocus(struct sway_container *con, void *data) {
@ -1116,15 +1141,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {
}
}
void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
if (seat->focused_layer) {
struct wlr_layer_surface_v1 *layer = seat->focused_layer;
seat_set_focus_layer(seat, NULL);
seat_set_focus(seat, node);
seat_set_focus_layer(seat, layer);
return;
}
static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) {
struct sway_node *last_focus = seat_get_focus(seat);
if (last_focus == node) {
return;
@ -1257,6 +1274,20 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
}
}
void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
if (seat->focused_layer) {
struct wlr_layer_surface_v1 *layer = seat->focused_layer;
seat_set_focus_layer(seat, NULL);
seat_set_workspace_focus(seat, node);
seat_set_focus_layer(seat, layer);
} else {
seat_set_workspace_focus(seat, node);
}
if (server.session_lock.locked) {
seat_set_focus_surface(seat, server.session_lock.focused, false);
}
}
void seat_set_focus_container(struct sway_seat *seat,
struct sway_container *con) {
seat_set_focus(seat, con ? &con->node : NULL);
@ -1561,7 +1592,7 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
}
void seatop_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event) {
struct wlr_pointer_axis_event *event) {
if (seat->seatop_impl->pointer_axis) {
seat->seatop_impl->pointer_axis(seat, event);
}
@ -1584,6 +1615,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
}
}
void seatop_hold_begin(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event) {
if (seat->seatop_impl->hold_begin) {
seat->seatop_impl->hold_begin(seat, event);
}
}
void seatop_hold_end(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event) {
if (seat->seatop_impl->hold_end) {
seat->seatop_impl->hold_end(seat, event);
}
}
void seatop_pinch_begin(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event) {
if (seat->seatop_impl->pinch_begin) {
seat->seatop_impl->pinch_begin(seat, event);
}
}
void seatop_pinch_update(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event) {
if (seat->seatop_impl->pinch_update) {
seat->seatop_impl->pinch_update(seat, event);
}
}
void seatop_pinch_end(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event) {
if (seat->seatop_impl->pinch_end) {
seat->seatop_impl->pinch_end(seat, event);
}
}
void seatop_swipe_begin(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event) {
if (seat->seatop_impl->swipe_begin) {
seat->seatop_impl->swipe_begin(seat, event);
}
}
void seatop_swipe_update(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event) {
if (seat->seatop_impl->swipe_update) {
seat->seatop_impl->swipe_update(seat, event);
}
}
void seatop_swipe_end(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event) {
if (seat->seatop_impl->swipe_end) {
seat->seatop_impl->swipe_end(seat, event);
}
}
void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
if (seat->seatop_impl->rebase) {
seat->seatop_impl->rebase(seat, time_msec);

View file

@ -4,6 +4,7 @@
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include "gesture.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/input/seat.h"
@ -20,6 +21,7 @@ struct seatop_default_event {
struct sway_node *previous_node;
uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
size_t pressed_button_count;
struct gesture_tracker gestures;
};
/*-----------------------------------------\
@ -427,13 +429,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
}
}
// Handle changing focus when clicking on a container
if (cont && state == WLR_BUTTON_PRESSED) {
// Default case: focus the container that was just clicked.
node = &cont->node;
// If the container is a tab/stacked container and the click happened
// on a tab, switch to the tab. If the tab contents were already
// focused, focus the tab container itself. If the tab container was
// already focused, cycle back to focusing the tab contents.
if (on_titlebar) {
struct sway_container *focus = seat_get_focused_container(seat);
if (focus == cont || !container_has_ancestor(focus, cont)) {
node = seat_get_focus_inactive(seat, &cont->node);
}
}
seat_set_focus(seat, node);
transaction_commit_dirty();
}
// Handle beginning floating move
if (cont && is_floating_or_child && !is_fullscreen_or_child &&
state == WLR_BUTTON_PRESSED) {
uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
if (button == btn_move && (mod_pressed || on_titlebar)) {
seat_set_focus_container(seat,
seat_get_focus_inactive_view(seat, &cont->node));
seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
return;
}
@ -444,6 +464,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
state == WLR_BUTTON_PRESSED) {
// Via border
if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
seat_set_focus_container(seat, cont);
seatop_begin_resize_floating(seat, cont, resize_edge);
return;
}
@ -458,6 +479,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
seat_set_focus_container(seat, floater);
seatop_begin_resize_floating(seat, floater, edge);
return;
}
@ -467,25 +489,18 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
if (config->tiling_drag && (mod_pressed || on_titlebar) &&
state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
struct sway_container *focus = seat_get_focused_container(seat);
bool focused = focus == cont || container_has_ancestor(focus, cont);
if (on_titlebar && !focused) {
node = seat_get_focus_inactive(seat, &cont->node);
seat_set_focus(seat, node);
}
// If moving a container by it's title bar, use a threshold for the drag
// If moving a container by its title bar, use a threshold for the drag
if (!mod_pressed && config->tiling_drag_threshold > 0) {
seatop_begin_move_tiling_threshold(seat, cont);
} else {
seatop_begin_move_tiling(seat, cont);
}
return;
}
// Handle mousedown on a container surface
if (surface && cont && state == WLR_BUTTON_PRESSED) {
seat_set_focus_container(seat, cont);
seatop_begin_down(seat, cont, time_msec, sx, sy);
seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
return;
@ -493,9 +508,6 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
// Handle clicking a container surface or decorations
if (cont && state == WLR_BUTTON_PRESSED) {
node = seat_get_focus_inactive(seat, &cont->node);
seat_set_focus(seat, node);
transaction_commit_dirty();
seat_pointer_notify_button(seat, time_msec, button, state);
return;
}
@ -645,7 +657,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
* Functions used by handle_pointer_axis /
*--------------------------------------*/
static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
switch (event->orientation) {
case WLR_AXIS_ORIENTATION_VERTICAL:
return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
@ -658,9 +670,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
}
static void handle_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event) {
struct wlr_pointer_axis_event *event) {
struct sway_input_device *input_device =
event->device ? event->device->data : NULL;
event->pointer ? event->pointer->base.data : NULL;
struct input_config *ic =
input_device ? input_device_get_config(input_device) : NULL;
struct sway_cursor *cursor = seat->cursor;
@ -706,6 +718,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
// Scrolling on a tabbed or stacked title bar (handled as press event)
if (!handled && (on_titlebar || on_titlebar_border)) {
struct sway_node *new_focus;
enum sway_container_layout layout = container_parent_layout(cont);
if (layout == L_TABBED || layout == L_STACKED) {
struct sway_node *tabcontainer = node_get_parent(node);
@ -713,7 +726,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
seat_get_active_tiling_child(seat, tabcontainer);
list_t *siblings = container_get_siblings(cont);
int desired = list_find(siblings, active->sway_container) +
round(scroll_factor * event->delta_discrete);
round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);
if (desired < 0) {
desired = 0;
} else if (desired >= siblings->length) {
@ -722,15 +735,17 @@ static void handle_pointer_axis(struct sway_seat *seat,
struct sway_container *new_sibling_con = siblings->items[desired];
struct sway_node *new_sibling = &new_sibling_con->node;
struct sway_node *new_focus =
seat_get_focus_inactive(seat, new_sibling);
// Use the focused child of the tabbed/stacked container, not the
// container the user scrolled on.
new_focus = seat_get_focus_inactive(seat, new_sibling);
} else {
new_focus = seat_get_focus_inactive(seat, &cont->node);
}
seat_set_focus(seat, new_focus);
transaction_commit_dirty();
handled = true;
}
}
// Handle mouse bindings - x11 mouse buttons 4-7 - release event
binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings,
@ -750,6 +765,304 @@ static void handle_pointer_axis(struct sway_seat *seat,
}
}
/*------------------------------------\
* Functions used by gesture support /
*----------------------------------*/
/**
* Check gesture binding for a specific gesture type and finger count.
* Returns true if binding is present, false otherwise
*/
static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
uint8_t fingers, struct sway_input_device *device) {
char *input =
device ? input_device_get_identifier(device->wlr_device) : strdup("*");
for (int i = 0; i < bindings->length; ++i) {
struct sway_gesture_binding *binding = bindings->items[i];
// Check type and finger count
if (!gesture_check(&binding->gesture, type, fingers)) {
continue;
}
// Check that input matches
if (strcmp(binding->input, "*") != 0 &&
strcmp(binding->input, input) != 0) {
continue;
}
free(input);
return true;
}
free(input);
return false;
}
/**
* Return the gesture binding which matches gesture type, finger count
* and direction, otherwise return null.
*/
static struct sway_gesture_binding* gesture_binding_match(
list_t *bindings, struct gesture *gesture, const char *input) {
struct sway_gesture_binding *current = NULL;
// Find best matching binding
for (int i = 0; i < bindings->length; ++i) {
struct sway_gesture_binding *binding = bindings->items[i];
bool exact = binding->flags & BINDING_EXACT;
// Check gesture matching
if (!gesture_match(&binding->gesture, gesture, exact)) {
continue;
}
// Check input matching
if (strcmp(binding->input, "*") != 0 &&
strcmp(binding->input, input) != 0) {
continue;
}
// If we already have a match ...
if (current) {
// ... check if input matching is equivalent
if (strcmp(current->input, binding->input) == 0) {
// ... - do not override an exact binding
if (!exact && current->flags & BINDING_EXACT) {
continue;
}
// ... - and ensure direction matching is better or equal
if (gesture_compare(&current->gesture, &binding->gesture) > 0) {
continue;
}
} else if (strcmp(binding->input, "*") == 0) {
// ... do not accept worse input match
continue;
}
}
// Accept newer or better match
current = binding;
// If exact binding and input is found, quit search
if (strcmp(current->input, input) == 0 &&
gesture_compare(&current->gesture, gesture) == 0) {
break;
}
} // for all gesture bindings
return current;
}
// Wrapper around gesture_tracker_end to use tracker with sway bindings
static struct sway_gesture_binding* gesture_tracker_end_and_match(
struct gesture_tracker *tracker, struct sway_input_device* device) {
// Determine name of input that received gesture
char *input = device
? input_device_get_identifier(device->wlr_device)
: strdup("*");
// Match tracking result to binding
struct gesture *gesture = gesture_tracker_end(tracker);
struct sway_gesture_binding *binding = gesture_binding_match(
config->current_mode->gesture_bindings, gesture, input);
free(gesture);
free(input);
return binding;
}
// Small wrapper around seat_execute_command to work on gesture bindings
static void gesture_binding_execute(struct sway_seat *seat,
struct sway_gesture_binding *binding) {
struct sway_binding *dummy_binding =
calloc(1, sizeof(struct sway_binding));
dummy_binding->type = BINDING_GESTURE;
dummy_binding->command = binding->command;
char *description = gesture_to_string(&binding->gesture);
sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
free(description);
seat_execute_command(seat, dummy_binding);
free(dummy_binding);
}
static void handle_hold_begin(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event) {
// Start tracking gesture if there is a matching binding ...
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
list_t *bindings = config->current_mode->gesture_bindings;
if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_hold_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
}
static void handle_hold_end(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event) {
// Ensure that gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_hold_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
return;
}
if (event->cancelled) {
gesture_tracker_cancel(&seatop->gestures);
return;
}
// End gesture tracking and execute matched binding
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
&seatop->gestures, device);
if (binding) {
gesture_binding_execute(seat, binding);
}
}
static void handle_pinch_begin(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event) {
// Start tracking gesture if there is a matching binding ...
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
list_t *bindings = config->current_mode->gesture_bindings;
if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_pinch_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
}
static void handle_pinch_update(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event) {
// Update any ongoing tracking ...
struct seatop_default_event *seatop = seat->seatop_data;
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
event->scale, event->rotation);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_pinch_update(
cursor->pointer_gestures,
cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy,
event->scale, event->rotation);
}
}
static void handle_pinch_end(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event) {
// Ensure that gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_pinch_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
return;
}
if (event->cancelled) {
gesture_tracker_cancel(&seatop->gestures);
return;
}
// End gesture tracking and execute matched binding
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
&seatop->gestures, device);
if (binding) {
gesture_binding_execute(seat, binding);
}
}
static void handle_swipe_begin(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event) {
// Start tracking gesture if there is a matching binding ...
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
list_t *bindings = config->current_mode->gesture_bindings;
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
}
static void handle_swipe_update(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event) {
// Update any ongoing tracking ...
struct seatop_default_event *seatop = seat->seatop_data;
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
gesture_tracker_update(&seatop->gestures,
event->dx, event->dy, NAN, NAN);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_update(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy);
}
}
static void handle_swipe_end(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event) {
// Ensure gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
return;
}
if (event->cancelled) {
gesture_tracker_cancel(&seatop->gestures);
return;
}
// End gesture tracking and execute matched binding
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
&seatop->gestures, device);
if (binding) {
gesture_binding_execute(seat, binding);
}
}
/*----------------------------------\
* Functions used by handle_rebase /
*--------------------------------*/
@ -779,6 +1092,14 @@ static const struct sway_seatop_impl seatop_impl = {
.pointer_axis = handle_pointer_axis,
.tablet_tool_tip = handle_tablet_tool_tip,
.tablet_tool_motion = handle_tablet_tool_motion,
.hold_begin = handle_hold_begin,
.hold_end = handle_hold_end,
.pinch_begin = handle_pinch_begin,
.pinch_update = handle_pinch_update,
.pinch_end = handle_pinch_end,
.swipe_begin = handle_swipe_begin,
.swipe_update = handle_swipe_update,
.swipe_end = handle_swipe_end,
.rebase = handle_rebase,
.allow_set_cursor = true,
};
@ -789,8 +1110,8 @@ void seatop_begin_default(struct sway_seat *seat) {
struct seatop_default_event *e =
calloc(1, sizeof(struct seatop_default_event));
sway_assert(e, "Unable to allocate seatop_default_event");
seat->seatop_impl = &seatop_impl;
seat->seatop_data = e;
seatop_rebase(seat, 0);
}

View file

@ -18,9 +18,9 @@ struct seatop_down_event {
};
static void handle_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event) {
struct wlr_pointer_axis_event *event) {
struct sway_input_device *input_device =
event->device ? event->device->data : NULL;
event->pointer ? event->pointer->base.data : NULL;
struct input_config *ic =
input_device ? input_device_get_config(input_device) : NULL;
float scroll_factor =

View file

@ -11,6 +11,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
return NULL;
}
device->switch_device = switch_device;
switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device);
switch_device->seat_device = device;
switch_device->state = WLR_SWITCH_STATE_OFF;
wl_list_init(&switch_device->switch_toggle.link);
@ -19,9 +20,23 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
return switch_device;
}
static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
enum wlr_switch_state state) {
switch (trigger) {
case SWAY_SWITCH_TRIGGER_ON:
return state == WLR_SWITCH_STATE_ON;
case SWAY_SWITCH_TRIGGER_OFF:
return state == WLR_SWITCH_STATE_OFF;
case SWAY_SWITCH_TRIGGER_TOGGLE:
return true;
}
abort(); // unreachable
}
static void execute_binding(struct sway_switch *sway_switch) {
struct sway_seat* seat = sway_switch->seat_device->sway_seat;
bool input_inhibited = seat->exclusive_client != NULL;
bool input_inhibited = seat->exclusive_client != NULL ||
server.session_lock.locked;
list_t *bindings = config->current_mode->switch_bindings;
struct sway_switch_binding *matched_binding = NULL;
@ -30,11 +45,10 @@ static void execute_binding(struct sway_switch *sway_switch) {
if (binding->type != sway_switch->type) {
continue;
}
if (binding->state != WLR_SWITCH_STATE_TOGGLE &&
binding->state != sway_switch->state) {
if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) {
continue;
}
if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE
if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE
|| (binding->flags & BINDING_RELOAD) == 0)) {
continue;
}
@ -65,7 +79,7 @@ static void execute_binding(struct sway_switch *sway_switch) {
static void handle_switch_toggle(struct wl_listener *listener, void *data) {
struct sway_switch *sway_switch =
wl_container_of(listener, sway_switch, switch_toggle);
struct wlr_event_switch_toggle *event = data;
struct wlr_switch_toggle_event *event = data;
struct sway_seat *seat = sway_switch->seat_device->sway_seat;
seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH);
@ -82,10 +96,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) {
}
void sway_switch_configure(struct sway_switch *sway_switch) {
struct wlr_input_device *wlr_device =
sway_switch->seat_device->input_device->wlr_device;
wl_list_remove(&sway_switch->switch_toggle.link);
wl_signal_add(&wlr_device->switch_device->events.toggle,
wl_signal_add(&sway_switch->wlr->events.toggle,
&sway_switch->switch_toggle);
sway_switch->switch_toggle.notify = handle_switch_toggle;
sway_log(SWAY_DEBUG, "Configured switch for device");

View file

@ -196,7 +196,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener,
static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);
struct wlr_event_tablet_pad_ring *event = data;
struct wlr_tablet_pad_ring_event *event = data;
if (!pad->current_surface) {
return;
@ -210,7 +210,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);
struct wlr_event_tablet_pad_strip *event = data;
struct wlr_tablet_pad_strip_event *event = data;
if (!pad->current_surface) {
return;
@ -224,7 +224,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
static void handle_tablet_pad_button(struct wl_listener *listener, void *data) {
struct sway_tablet_pad *pad = wl_container_of(listener, pad, button);
struct wlr_event_tablet_pad_button *event = data;
struct wlr_tablet_pad_button_event *event = data;
if (!pad->current_surface) {
return;
@ -246,6 +246,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
return NULL;
}
tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device);
tablet_pad->seat_device = device;
wl_list_init(&tablet_pad->attach.link);
wl_list_init(&tablet_pad->button.link);
@ -260,40 +261,40 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
}
void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
struct wlr_input_device *device =
struct wlr_input_device *wlr_device =
tablet_pad->seat_device->input_device->wlr_device;
struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
if (!tablet_pad->tablet_v2_pad) {
tablet_pad->tablet_v2_pad =
wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device);
wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device);
}
wl_list_remove(&tablet_pad->attach.link);
tablet_pad->attach.notify = handle_tablet_pad_attach;
wl_signal_add(&device->tablet_pad->events.attach_tablet,
wl_signal_add(&tablet_pad->wlr->events.attach_tablet,
&tablet_pad->attach);
wl_list_remove(&tablet_pad->button.link);
tablet_pad->button.notify = handle_tablet_pad_button;
wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button);
wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button);
wl_list_remove(&tablet_pad->strip.link);
tablet_pad->strip.notify = handle_tablet_pad_strip;
wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip);
wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip);
wl_list_remove(&tablet_pad->ring.link);
tablet_pad->ring.notify = handle_tablet_pad_ring;
wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring);
wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);
/* Search for a sibling tablet */
if (!wlr_input_device_is_libinput(device)) {
if (!wlr_input_device_is_libinput(wlr_device)) {
/* We can only do this on libinput devices */
return;
}
struct libinput_device_group *group =
libinput_device_get_device_group(wlr_libinput_get_device_handle(device));
libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device));
struct sway_tablet *tool;
wl_list_for_each(tool, &seat->cursor->tablets, link) {
struct wlr_input_device *tablet =

View file

@ -77,8 +77,6 @@ static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) {
struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat);
wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,
active_keyboard);
wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab,
&active_keyboard->modifiers);
wl_signal_add(&keyboard_grab->events.destroy,
&relay->input_method_keyboard_grab_destroy);

View file

@ -112,12 +112,43 @@ static const char *ipc_json_output_adaptive_sync_status_description(
return "disabled";
case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED:
return "enabled";
case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN:
return "unknown";
}
return NULL;
}
static const char *ipc_json_output_mode_aspect_ratio_description(
enum wlr_output_mode_aspect_ratio aspect_ratio) {
switch (aspect_ratio) {
case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE:
return "none";
case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3:
return "4:3";
case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9:
return "16:9";
case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27:
return "64:27";
case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135:
return "256:135";
}
return NULL;
}
static json_object *ipc_json_output_mode_description(
const struct wlr_output_mode *mode) {
const char *pic_ar =
ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio);
json_object *mode_object = json_object_new_object();
json_object_object_add(mode_object, "width",
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object_object_add(mode_object, "picture_aspect_ratio",
json_object_new_string(pic_ar));
return mode_object;
}
#if HAVE_XWAYLAND
static const char *ipc_json_xwindow_type_description(struct sway_view *view) {
struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
@ -242,23 +273,50 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
return object;
}
static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {
json_object_object_add(object, "primary", json_object_new_boolean(false));
json_object_object_add(object, "make",
json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
json_object_object_add(object, "model",
json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown"));
json_object_object_add(object, "serial",
json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown"));
json_object *modes_array = json_object_new_array();
struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link) {
json_object *mode_object = json_object_new_object();
json_object_object_add(mode_object, "width",
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object_array_add(modes_array, mode_object);
}
json_object_object_add(object, "modes", modes_array);
}
static void ipc_json_describe_output(struct sway_output *output,
json_object *object) {
ipc_json_describe_wlr_output(output->wlr_output, object);
}
static void ipc_json_describe_enabled_output(struct sway_output *output,
json_object *object) {
ipc_json_describe_output(output, object);
struct wlr_output *wlr_output = output->wlr_output;
json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
json_object_object_add(object, "active", json_object_new_boolean(true));
json_object_object_add(object, "dpms",
json_object_new_boolean(wlr_output->enabled));
json_object_object_add(object, "primary", json_object_new_boolean(false));
json_object_object_add(object, "power",
json_object_new_boolean(wlr_output->enabled));
json_object_object_add(object, "layout", json_object_new_string("output"));
json_object_object_add(object, "orientation",
json_object_new_string(
ipc_json_orientation_description(L_NONE)));
json_object_object_add(object, "make",
json_object_new_string(wlr_output->make));
json_object_object_add(object, "model",
json_object_new_string(wlr_output->model));
json_object_object_add(object, "serial",
json_object_new_string(wlr_output->serial));
json_object_object_add(object, "scale",
json_object_new_double(wlr_output->scale));
json_object_object_add(object, "scale_filter",
@ -283,25 +341,26 @@ static void ipc_json_describe_output(struct sway_output *output,
json_object *modes_array = json_object_new_array();
struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link) {
json_object *mode_object = json_object_new_object();
json_object_object_add(mode_object, "width",
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object *mode_object =
ipc_json_output_mode_description(mode);
json_object_array_add(modes_array, mode_object);
}
json_object_object_add(object, "modes", modes_array);
json_object *current_mode_object = json_object_new_object();
json_object *current_mode_object;
if (wlr_output->current_mode != NULL) {
current_mode_object =
ipc_json_output_mode_description(wlr_output->current_mode);
} else {
current_mode_object = json_object_new_object();
json_object_object_add(current_mode_object, "width",
json_object_new_int(wlr_output->width));
json_object_object_add(current_mode_object, "height",
json_object_new_int(wlr_output->height));
json_object_object_add(current_mode_object, "refresh",
json_object_new_int(wlr_output->refresh));
}
json_object_object_add(object, "current_mode", current_mode_object);
struct sway_node *parent = node_get_parent(&output->node);
@ -325,33 +384,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
json_object *object = json_object_new_object();
ipc_json_describe_output(output, object);
json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
json_object_object_add(object, "type", json_object_new_string("output"));
json_object_object_add(object, "name",
json_object_new_string(wlr_output->name));
json_object_object_add(object, "active", json_object_new_boolean(false));
json_object_object_add(object, "dpms", json_object_new_boolean(false));
json_object_object_add(object, "primary", json_object_new_boolean(false));
json_object_object_add(object, "make",
json_object_new_string(wlr_output->make));
json_object_object_add(object, "model",
json_object_new_string(wlr_output->model));
json_object_object_add(object, "serial",
json_object_new_string(wlr_output->serial));
json_object *modes_array = json_object_new_array();
struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link) {
json_object *mode_object = json_object_new_object();
json_object_object_add(mode_object, "width",
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object_array_add(modes_array, mode_object);
}
json_object_object_add(object, "modes", modes_array);
json_object_object_add(object, "power", json_object_new_boolean(false));
json_object_object_add(object, "current_workspace", NULL);
@ -367,6 +408,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
return object;
}
json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) {
struct wlr_output *wlr_output = output->wlr_output;
json_object *object = json_object_new_object();
ipc_json_describe_wlr_output(wlr_output, object);
json_object_object_add(object, "non_desktop", json_object_new_boolean(true));
json_object_object_add(object, "type", json_object_new_string("output"));
json_object_object_add(object, "name",
json_object_new_string(wlr_output->name));
return object;
}
static json_object *ipc_json_describe_scratchpad_output(void) {
struct wlr_box box;
root_get_box(root, &box);
@ -453,7 +509,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
enum sway_container_layout parent_layout = container_parent_layout(c);
bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED;
list_t *siblings = container_get_siblings(c);
bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED)
&& ((siblings && siblings->length > 1) || !config->hide_lone_tab);
if (((!tab_or_stack || container_is_floating(c)) &&
c->current.border != B_NORMAL) ||
c->pending.fullscreen_mode != FULLSCREEN_NONE ||
@ -706,7 +764,7 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
case N_ROOT:
break;
case N_OUTPUT:
ipc_json_describe_output(node->sway_output, object);
ipc_json_describe_enabled_output(node->sway_output, object);
break;
case N_CONTAINER:
ipc_json_describe_container(node->sway_container, object);
@ -942,6 +1000,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
json_object_object_add(object, "dwt", json_object_new_string(dwt));
}
if (libinput_device_config_dwtp_is_available(device)) {
const char *dwtp = "unknown";
switch (libinput_device_config_dwtp_get_enabled(device)) {
case LIBINPUT_CONFIG_DWTP_ENABLED:
dwtp = "enabled";
break;
case LIBINPUT_CONFIG_DWTP_DISABLED:
dwtp = "disabled";
break;
}
json_object_object_add(object, "dwtp", json_object_new_string(dwtp));
}
if (libinput_device_config_calibration_has_matrix(device)) {
float matrix[6];
libinput_device_config_calibration_get_matrix(device, matrix);
@ -977,10 +1048,16 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
input_device_get_type(device)));
if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
struct wlr_keyboard *keyboard = device->wlr_device->keyboard;
struct wlr_keyboard *keyboard =
wlr_keyboard_from_input_device(device->wlr_device);
struct xkb_keymap *keymap = keyboard->keymap;
struct xkb_state *state = keyboard->xkb_state;
json_object_object_add(object, "repeat_delay",
json_object_new_int(keyboard->repeat_info.delay));
json_object_object_add(object, "repeat_rate",
json_object_new_int(keyboard->repeat_info.rate));
json_object *layouts_arr = json_object_new_array();
json_object_object_add(object, "xkb_layout_names", layouts_arr);

View file

@ -150,7 +150,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
int ipc_handle_connection(int fd, uint32_t mask, void *data) {
(void) fd;
struct sway_server *server = data;
sway_log(SWAY_DEBUG, "Event on IPC listening socket");
assert(mask == WL_EVENT_READABLE);
int client_fd = accept(ipc_socket, NULL, NULL);
@ -211,13 +210,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
}
if (mask & WL_EVENT_HANGUP) {
sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client);
return 0;
}
sway_log(SWAY_DEBUG, "Client %d readable", client->fd);
int read_available;
if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size");
@ -523,7 +519,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
}
if (mask & WL_EVENT_HANGUP) {
sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client);
return 0;
}
@ -532,8 +527,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
return 0;
}
sway_log(SWAY_DEBUG, "Client %d writable", client->fd);
ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
if (written == -1 && errno == EAGAIN) {
@ -692,6 +685,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_json_describe_disabled_output(output));
}
}
for (int i = 0; i < root->non_desktop_outputs->length; i++) {
struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i];
json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output));
}
const char *json_string = json_object_to_json_string(outputs);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
@ -955,7 +954,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ
ipc_client_handle_writable, client);
}
sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s",
payload_type, client->fd, payload);
return true;
}

225
sway/lock.c Normal file
View file

@ -0,0 +1,225 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include "log.h"
#include "sway/input/keyboard.h"
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/server.h"
struct sway_session_lock_surface {
struct wlr_session_lock_surface_v1 *lock_surface;
struct sway_output *output;
struct wlr_surface *surface;
struct wl_listener map;
struct wl_listener destroy;
struct wl_listener surface_commit;
struct wl_listener output_mode;
struct wl_listener output_commit;
struct wl_listener output_destroy;
};
static void set_lock_focused_surface(struct wlr_surface *focused) {
server.session_lock.focused = focused;
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat_set_focus_surface(seat, focused, false);
}
}
static void handle_surface_map(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map);
if (server.session_lock.focused == NULL) {
set_lock_focused_surface(surf->surface);
}
output_damage_whole(surf->output);
}
static void handle_surface_commit(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit);
output_damage_surface(surf->output, 0, 0, surf->surface, false);
}
static void handle_output_mode(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode);
wlr_session_lock_surface_v1_configure(surf->lock_surface,
surf->output->width, surf->output->height);
}
static void handle_output_commit(struct wl_listener *listener, void *data) {
struct wlr_output_event_commit *event = data;
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit);
if (event->committed & (
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_SCALE |
WLR_OUTPUT_STATE_TRANSFORM)) {
wlr_session_lock_surface_v1_configure(surf->lock_surface,
surf->output->width, surf->output->height);
}
}
static void destroy_lock_surface(struct sway_session_lock_surface *surf) {
// Move the seat focus to another surface if one is available
if (server.session_lock.focused == surf->surface) {
struct wlr_surface *next_focus = NULL;
struct wlr_session_lock_surface_v1 *other;
wl_list_for_each(other, &server.session_lock.lock->surfaces, link) {
if (other != surf->lock_surface && other->mapped) {
next_focus = other->surface;
break;
}
}
set_lock_focused_surface(next_focus);
}
wl_list_remove(&surf->map.link);
wl_list_remove(&surf->destroy.link);
wl_list_remove(&surf->surface_commit.link);
wl_list_remove(&surf->output_mode.link);
wl_list_remove(&surf->output_commit.link);
wl_list_remove(&surf->output_destroy.link);
output_damage_whole(surf->output);
free(surf);
}
static void handle_surface_destroy(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy);
destroy_lock_surface(surf);
}
static void handle_output_destroy(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf =
wl_container_of(listener, surf, output_destroy);
destroy_lock_surface(surf);
}
static void handle_new_surface(struct wl_listener *listener, void *data) {
struct wlr_session_lock_surface_v1 *lock_surface = data;
struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf));
if (surf == NULL) {
return;
}
sway_log(SWAY_DEBUG, "new lock layer surface");
struct sway_output *output = lock_surface->output->data;
wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height);
surf->lock_surface = lock_surface;
surf->surface = lock_surface->surface;
surf->output = output;
surf->map.notify = handle_surface_map;
wl_signal_add(&lock_surface->events.map, &surf->map);
surf->destroy.notify = handle_surface_destroy;
wl_signal_add(&lock_surface->events.destroy, &surf->destroy);
surf->surface_commit.notify = handle_surface_commit;
wl_signal_add(&surf->surface->events.commit, &surf->surface_commit);
surf->output_mode.notify = handle_output_mode;
wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode);
surf->output_commit.notify = handle_output_commit;
wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit);
surf->output_destroy.notify = handle_output_destroy;
wl_signal_add(&output->node.events.destroy, &surf->output_destroy);
}
static void handle_unlock(struct wl_listener *listener, void *data) {
sway_log(SWAY_DEBUG, "session unlocked");
server.session_lock.locked = false;
server.session_lock.lock = NULL;
server.session_lock.focused = NULL;
wl_list_remove(&server.session_lock.lock_new_surface.link);
wl_list_remove(&server.session_lock.lock_unlock.link);
wl_list_remove(&server.session_lock.lock_destroy.link);
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat_set_exclusive_client(seat, NULL);
// copied from seat_set_focus_layer -- deduplicate?
struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
if (previous) {
// Hack to get seat to re-focus the return value of get_focus
seat_set_focus(seat, NULL);
seat_set_focus(seat, previous);
}
}
// redraw everything
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_whole(output);
}
}
static void handle_abandon(struct wl_listener *listener, void *data) {
sway_log(SWAY_INFO, "session lock abandoned");
server.session_lock.lock = NULL;
server.session_lock.focused = NULL;
wl_list_remove(&server.session_lock.lock_new_surface.link);
wl_list_remove(&server.session_lock.lock_unlock.link);
wl_list_remove(&server.session_lock.lock_destroy.link);
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat->exclusive_client = NULL;
}
// redraw everything
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_whole(output);
}
}
static void handle_session_lock(struct wl_listener *listener, void *data) {
struct wlr_session_lock_v1 *lock = data;
struct wl_client *client = wl_resource_get_client(lock->resource);
if (server.session_lock.lock) {
wlr_session_lock_v1_destroy(lock);
return;
}
sway_log(SWAY_DEBUG, "session locked");
server.session_lock.locked = true;
server.session_lock.lock = lock;
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat_set_exclusive_client(seat, client);
}
wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface);
wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock);
wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy);
wlr_session_lock_v1_send_locked(lock);
// redraw everything
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_whole(output);
}
}
static void handle_session_lock_destroy(struct wl_listener *listener, void *data) {
assert(server.session_lock.lock == NULL);
wl_list_remove(&server.session_lock.new_lock.link);
wl_list_remove(&server.session_lock.manager_destroy.link);
}
void sway_session_lock_init(void) {
server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);
server.session_lock.lock_new_surface.notify = handle_new_surface;
server.session_lock.lock_unlock.notify = handle_unlock;
server.session_lock.lock_destroy.notify = handle_abandon;
server.session_lock.new_lock.notify = handle_session_lock;
server.session_lock.manager_destroy.notify = handle_session_lock_destroy;
wl_signal_add(&server.session_lock.manager->events.new_lock,
&server.session_lock.new_lock);
wl_signal_add(&server.session_lock.manager->events.destroy,
&server.session_lock.manager_destroy);
}

View file

@ -150,27 +150,17 @@ static void log_kernel(void) {
pclose(f);
}
static bool detect_suid(void) {
if (geteuid() != 0 && getegid() != 0) {
return false;
}
static bool drop_permissions(void) {
if (getuid() != geteuid() || getgid() != getegid()) {
sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: "
"SUID privilege drop will be removed in a future release, please migrate to seatd-launch");
if (getuid() == geteuid() && getgid() == getegid()) {
return false;
}
// Set the gid and uid in the correct order.
if (setgid(getgid()) != 0) {
sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
return false;
}
if (setuid(getuid()) != 0) {
sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start");
return false;
}
}
if (setgid(0) != -1 || setuid(0) != -1) {
sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to "
"restore it after setuid), refusing to start");
return false;
}
sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. "
"This check will be removed in a future release.");
return true;
}
@ -240,9 +230,6 @@ static void handle_wlr_log(enum wlr_log_importance importance,
_sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);
}
int main(int argc, char **argv) {
static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0;
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"config", required_argument, NULL, 'c'},
@ -255,9 +242,7 @@ int main(int argc, char **argv) {
{0, 0, 0, 0}
};
char *config_path = NULL;
const char* usage =
static const char usage[] =
"Usage: sway [options] [command]\n"
"\n"
" -h, --help Show help message and quit.\n"
@ -269,6 +254,11 @@ int main(int argc, char **argv) {
" --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
"\n";
int main(int argc, char **argv) {
static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false;
char *config_path = NULL;
int c;
while (1) {
int option_index = 0;
@ -286,25 +276,25 @@ int main(int argc, char **argv) {
config_path = strdup(optarg);
break;
case 'C': // validate
validate = 1;
validate = true;
break;
case 'd': // debug
debug = 1;
debug = true;
break;
case 'D': // extended debug options
enable_debug_flag(optarg);
break;
case 'u':
allow_unsupported_gpu = 1;
allow_unsupported_gpu = true;
break;
case 'v': // version
printf("sway version " SWAY_VERSION "\n");
exit(EXIT_SUCCESS);
break;
case 'V': // verbose
verbose = 1;
verbose = true;
break;
case 'p': ; // --get-socketpath
case 'p': // --get-socketpath
if (getenv("SWAYSOCK")) {
printf("%s\n", getenv("SWAYSOCK"));
exit(EXIT_SUCCESS);
@ -319,6 +309,11 @@ int main(int argc, char **argv) {
}
}
// SUID operation is deprecated, so block it for now.
if (detect_suid()) {
exit(EXIT_FAILURE);
}
// Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the
// clear error message (when not running as an IPC client).
if (!getenv("XDG_RUNTIME_DIR") && optind == argc) {
@ -357,9 +352,6 @@ int main(int argc, char **argv) {
"`sway -d 2>sway.log`.");
exit(EXIT_FAILURE);
}
if (!drop_permissions()) {
exit(EXIT_FAILURE);
}
char *socket_path = getenv("SWAYSOCK");
if (!socket_path) {
sway_log(SWAY_ERROR, "Unable to retrieve socket path");
@ -372,16 +364,6 @@ int main(int argc, char **argv) {
}
detect_proprietary(allow_unsupported_gpu);
if (!server_privileged_prepare(&server)) {
return 1;
}
if (!drop_permissions()) {
server_fini(&server);
exit(EXIT_FAILURE);
}
increase_nofile_limit();
// handle SIGTERM signals
@ -413,6 +395,8 @@ int main(int argc, char **argv) {
goto shutdown;
}
set_rr_scheduling();
if (!server_start(&server)) {
sway_terminate(EXIT_FAILURE);
goto shutdown;

View file

@ -5,7 +5,9 @@ sway_sources = files(
'decoration.c',
'ipc-json.c',
'ipc-server.c',
'lock.c',
'main.c',
'realtime.c',
'server.c',
'swaynag.c',
'xdg_activation_v1.c',
@ -20,7 +22,7 @@ sway_sources = files(
'desktop/surface.c',
'desktop/transaction.c',
'desktop/xdg_shell.c',
'desktop/launcher.c',
'input/input-manager.c',
'input/cursor.c',
'input/keyboard.c',
@ -69,13 +71,13 @@ sway_sources = files(
'commands/force_focus_wrapping.c',
'commands/fullscreen.c',
'commands/gaps.c',
'commands/gesture.c',
'commands/hide_edge_borders.c',
'commands/inhibit_idle.c',
'commands/kill.c',
'commands/mark.c',
'commands/max_render_time.c',
'commands/opacity.c',
'commands/saturation.c',
'commands/include.c',
'commands/input.c',
'commands/layout.c',
@ -91,6 +93,7 @@ sway_sources = files(
'commands/reload.c',
'commands/rename.c',
'commands/resize.c',
'commands/saturation.c',
'commands/scratchpad.c',
'commands/seat.c',
'commands/seat/attach.c',
@ -159,6 +162,7 @@ sway_sources = files(
'commands/input/drag.c',
'commands/input/drag_lock.c',
'commands/input/dwt.c',
'commands/input/dwtp.c',
'commands/input/events.c',
'commands/input/left_handed.c',
'commands/input/map_from_region.c',
@ -193,12 +197,14 @@ sway_sources = files(
'commands/output/max_render_time.c',
'commands/output/mode.c',
'commands/output/position.c',
'commands/output/power.c',
'commands/output/render_bit_depth.c',
'commands/output/scale.c',
'commands/output/scale_filter.c',
'commands/output/subpixel.c',
'commands/output/toggle.c',
'commands/output/transform.c',
'commands/output/unplug.c',
'tree/arrange.c',
'tree/container.c',
@ -220,13 +226,14 @@ sway_deps = [
libudev,
math,
pango,
pcre,
pcre2,
glesv2,
pixman,
server_protos,
threads,
wayland_server,
wlroots,
xkbcommon,
xcb_icccm,
egl,
]
@ -237,7 +244,7 @@ endif
executable(
'sway',
sway_sources,
sway_sources + wl_protos_src,
include_directories: [sway_inc],
dependencies: sway_deps,
link_with: [lib_sway_common],

40
sway/realtime.c Normal file
View file

@ -0,0 +1,40 @@
#include <sys/resource.h>
#include <sched.h>
#include <unistd.h>
#include <pthread.h>
#include "sway/server.h"
#include "log.h"
static void child_fork_callback(void) {
struct sched_param param;
param.sched_priority = 0;
int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, &param);
if (ret != 0) {
sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork");
}
}
void set_rr_scheduling(void) {
int prio = sched_get_priority_min(SCHED_RR);
int old_policy;
int ret;
struct sched_param param;
ret = pthread_getschedparam(pthread_self(), &old_policy, &param);
if (ret != 0) {
sway_log(SWAY_DEBUG, "Failed to get old scheduling priority");
return;
}
param.sched_priority = prio;
ret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
if (ret != 0) {
sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio);
return;
}
pthread_atfork(NULL, NULL, child_fork_callback);
}

View file

@ -18,13 +18,16 @@
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_xcursor_manager.h>
@ -48,19 +51,6 @@
#include "sway/xwayland.h"
#endif
bool server_privileged_prepare(struct sway_server *server) {
sway_log(SWAY_DEBUG, "Preparing Wayland server initialization");
server->wl_display = wl_display_create();
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
server->backend = wlr_backend_autocreate(server->wl_display);
if (!server->backend) {
sway_log(SWAY_ERROR, "Unable to create backend");
return false;
}
return true;
}
static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
/* We only offer non-desktop outputs, but in the future we might want to do
* more logic here. */
@ -73,8 +63,18 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
}
}
#define SWAY_XDG_SHELL_VERSION 2
bool server_init(struct sway_server *server) {
sway_log(SWAY_DEBUG, "Initializing Wayland server");
server->wl_display = wl_display_create();
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
server->backend = wlr_backend_autocreate(server->wl_display);
if (!server->backend) {
sway_log(SWAY_ERROR, "Unable to create backend");
return false;
}
server->wlr_renderer = wlr_renderer_autocreate(server->backend);
if (!server->wlr_renderer) {
@ -109,6 +109,8 @@ bool server_init(struct sway_server *server) {
wl_signal_add(&server->compositor->events.new_surface,
&server->compositor_new_surface);
wlr_subcompositor_create(server->wl_display);
server->data_device_manager =
wlr_data_device_manager_create(server->wl_display);
@ -123,6 +125,7 @@ bool server_init(struct sway_server *server) {
wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
server->idle = wlr_idle_create(server->wl_display);
server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);
server->idle_inhibit_manager_v1 =
sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle);
@ -131,7 +134,8 @@ bool server_init(struct sway_server *server) {
&server->layer_shell_surface);
server->layer_shell_surface.notify = handle_layer_shell_surface;
server->xdg_shell = wlr_xdg_shell_create(server->wl_display);
server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
SWAY_XDG_SHELL_VERSION);
wl_signal_add(&server->xdg_shell->events.new_surface,
&server->xdg_shell_surface);
server->xdg_shell_surface.notify = handle_xdg_shell_surface;
@ -188,6 +192,8 @@ bool server_init(struct sway_server *server) {
server->foreign_toplevel_manager =
wlr_foreign_toplevel_manager_v1_create(server->wl_display);
sway_session_lock_init();
server->drm_lease_manager=
wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
if (server->drm_lease_manager) {
@ -204,6 +210,7 @@ bool server_init(struct sway_server *server) {
wlr_data_control_manager_v1_create(server->wl_display);
wlr_primary_selection_v1_device_manager_create(server->wl_display);
wlr_viewporter_create(server->wl_display);
wlr_single_pixel_buffer_manager_v1_create(server->wl_display);
struct wlr_xdg_foreign_registry *foreign_registry =
wlr_xdg_foreign_registry_create(server->wl_display);
@ -216,10 +223,12 @@ bool server_init(struct sway_server *server) {
wl_signal_add(&server->xdg_activation_v1->events.request_activate,
&server->xdg_activation_v1_request_activate);
wl_list_init(&server->pending_launcher_ctxs);
// Avoid using "wayland-0" as display socket
char name_candidate[16];
for (int i = 1; i <= 32; ++i) {
sprintf(name_candidate, "wayland-%d", i);
for (unsigned int i = 1; i <= 32; ++i) {
snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {
server->socket = strdup(name_candidate);
break;

View file

@ -147,6 +147,10 @@ The following commands may only be used in the configuration file.
*input* <identifier> dwt enabled|disabled
Enables or disables disable-while-typing for the specified input device.
*input* <identifier> dwtp enabled|disabled
Enables or disables disable-while-trackpointing for the specified input
device.
*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>]
Enables or disables send_events for specified input device. Disabling
send_events disables the input device.

View file

@ -213,7 +213,10 @@ following properties:
: Whether this output is active/enabled
|- dpms
: boolean
: Whether this output is on/off (via DPMS)
: (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS)
|- power
: boolean
: Whether this output is on/off
|- primary
: boolean
: For i3 compatibility, this will be false. It does not make sense in Wayland
@ -1194,6 +1197,10 @@ following properties will be included for devices that support them:
|- dwt
: string
: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_
|- dwtp
: string
: Whether disable-while-trackpointing is enabled. It can be _enabled_ or
_disabled_
|- calibration_matrix
: array
: An array of 6 floats representing the calibration matrix for absolute
@ -1233,7 +1240,8 @@ following properties will be included for devices that support them:
"click_method": "button_areas",
"middle_emulation": "disabled",
"scroll_method": "edge",
"dwt": "enabled"
"dwt": "enabled",
"dwtp": "enabled"
}
},
{
@ -1360,7 +1368,8 @@ one seat. Each object has the following properties:
"click_method": "button_areas",
"middle_emulation": "disabled",
"scroll_method": "edge",
"dwt": "enabled"
"dwt": "enabled",
"dwtp": "enabled"
}
},
{

View file

@ -24,7 +24,7 @@ must be separated by one space. For example:
# COMMANDS
*output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz]
*output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz]
Configures the specified output to use the given mode. Modes are a
combination of width and height (in pixels) and a refresh rate that your
display can be configured to use. For a list of available modes for each
@ -119,12 +119,20 @@ must be separated by one space. For example:
Enables or disables the specified output (all outputs are enabled by
default).
As opposed to the _power_ command, the output will loose its current
workspace and windows.
*output* <name> toggle
Toggle the specified output.
*output* <name> power on|off|toggle
Turns on or off the specified output.
As opposed to the _enable_ and _disable_ commands, the output keeps its
current workspaces and windows.
*output* <name> dpms on|off|toggle
Enables or disables the specified output via DPMS. To turn an output off
(ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
Deprecated. Alias for _power_.
*output* <name> max_render_time off|<msec>
Controls when sway composites the output, as a positive number of

View file

@ -176,6 +176,12 @@ set|plus|minus|toggle <amount>
*layout* default|splith|splitv|stacking|tabbed
Sets the layout mode of the focused container.
When using the _stacking_ layout, only the focused window in the container is
displayed, with the opened windows' list on the top of the container.
The _tabbed_ layout is similar to _stacking_, but the windows list is vertically
split.
*layout* toggle [split|all]
Cycles the layout mode of the focused container though a preset list of
layouts. If no argument is given, then it cycles through stacking, tabbed
@ -210,10 +216,9 @@ set|plus|minus|toggle <amount>
further details.
*move* left|right|up|down [<px> px]
Moves the focused container in the direction specified. If the container,
the optional _px_ argument specifies how many pixels to move the container.
If unspecified, the default is 10 pixels. Pixels are ignored when moving
tiled containers.
Moves the focused container in the direction specified. The optional _px_
argument specifies how many pixels to move the container. If unspecified,
the default is 10 pixels. Pixels are ignored when moving tiled containers.
*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
Moves the focused container to the specified position in the workspace.
@ -482,6 +487,62 @@ runtime.
bindswitch lid:toggle exec echo "Lid moved"
```
*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \
<gesture>[:<fingers>][:directions] <command>
Binds _gesture_ to execute the sway command _command_ when detected.
Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally
can be limited to bind to a certain number of _fingers_ or, for a
_pinch_ or _swipe_ gesture, to certain _directions_.
[[ *type*
:[ *fingers*
:< *direction*
| hold
:- 1 - 5
: none
| swipe
: 3 - 5
: up, down, left, right
| pinch
: 2 - 5
: all above + inward, outward, clockwise, counterclockwise
The _fingers_ can be limited to any sensible number or left empty to accept
any finger counts.
Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_,
_outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.
Multiple directions can be combined by a plus.
If a _input-device_ is given, the binding will only be executed for
that input device and will be executed instead of any binding that is
generic to all devices. By default, if you overwrite a binding,
swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
The _--exact_ flag can be used to ensure a binding only matches when exactly
all specified directions are matched and nothing more. If there is matching
binding with _--exact_, it will be preferred.
The priority for matching bindings is as follows: input device, then
exact matches followed by matches with the highest number of matching
directions.
Gestures executed while the pointer is above a bar are not handled by sway.
See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
Example:
```
# Allow switching between workspaces with left and right swipes
bindgesture swipe:right workspace prev
bindgesture swipe:left workspace next
# Allow container movements by pinching them
bindgesture pinch:inward+up move up
bindgesture pinch:inward+down move down
bindgesture pinch:inward+left move left
bindgesture pinch:inward+right move right
```
*client.background* <color>
This command is ignored and is only present for i3 compatibility.
@ -632,11 +693,11 @@ The default colors are:
after switching between workspaces.
*focus_on_window_activation* smart|urgent|focus|none
This option determines what to do when an xwayland client requests
window activation. If set to _urgent_, the urgent state will be set
for that window. If set to _focus_, the window will become focused.
If set to _smart_, the window will become focused only if it is already
visible, otherwise the urgent state will be set. Default is _urgent_.
This option determines what to do when a client requests window activation.
If set to _urgent_, the urgent state will be set for that window. If set to
_focus_, the window will become focused. If set to _smart_, the window will
become focused only if it is already visible, otherwise the urgent state
will be set. Default is _urgent_.
*focus_wrapping* yes|no|force|workspace
This option determines what to do when attempting to focus over the edge
@ -805,6 +866,11 @@ The default colors are:
*unbindswitch* <switch>:<state>
Removes a binding for when <switch> changes to <state>.
*unbindgesture* [--exact] [--input-device=<device>] \
<gesture>[:<fingers>][:directions]
Removes a binding for the specified _gesture_, _fingers_
and _directions_ combination.
*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
[--to-code] [--input-device=<device>] <key combo>
Removes the binding for _key combo_ that was previously bound with the
@ -918,7 +984,8 @@ The following attributes may be matched with:
*class*
Compare value against the window class. Can be a regular expression. If
value is \_\_focused\_\_, then the window class must be the same as that of
the currently focused window. _class_ are specific to X11 applications.
the currently focused window. _class_ are specific to X11 applications and
require XWayland.
*con_id*
Compare against the internal container ID, which you can find via IPC. If
@ -932,12 +999,14 @@ The following attributes may be matched with:
Matches floating windows.
*id*
Compare value against the X11 window ID. Must be numeric.
Compare value against the X11 window ID. Must be numeric. id is specific to
X11 applications and requires XWayland.
*instance*
Compare value against the window instance. Can be a regular expression. If
value is \_\_focused\_\_, then the window instance must be the same as that
of the currently focused window.
of the currently focused window. instance is specific to X11 applications and
requires XWayland.
*pid*
Compare value against the window's process ID. Must be numeric.
@ -962,12 +1031,14 @@ The following attributes may be matched with:
*window_role*
Compare against the window role (WM_WINDOW_ROLE). Can be a regular
expression. If value is \_\_focused\_\_, then the window role must be the
same as that of the currently focused window.
same as that of the currently focused window. window_role is specific to X11
applications and requires XWayland.
*window_type*
Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values
are normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
popup_menu, tooltip and notification.
popup_menu, tooltip and notification. window_type is specific to X11
applications and requires XWayland.
*workspace*
Compare against the workspace name for this view. Can be a regular

View file

@ -311,12 +311,13 @@ void arrange_output(struct sway_output *output) {
if (config->reloading) {
return;
}
const struct wlr_box *output_box = wlr_output_layout_get_box(
root->output_layout, output->wlr_output);
output->lx = output_box->x;
output->ly = output_box->y;
output->width = output_box->width;
output->height = output_box->height;
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout,
output->wlr_output, &output_box);
output->lx = output_box.x;
output->ly = output_box.y;
output->width = output_box.width;
output->height = output_box.height;
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
@ -328,12 +329,12 @@ void arrange_root(void) {
if (config->reloading) {
return;
}
const struct wlr_box *layout_box =
wlr_output_layout_get_box(root->output_layout, NULL);
root->x = layout_box->x;
root->y = layout_box->y;
root->width = layout_box->width;
root->height = layout_box->height;
struct wlr_box layout_box;
wlr_output_layout_get_box(root->output_layout, NULL, &layout_box);
root->x = layout_box.x;
root->y = layout_box.y;
root->width = layout_box.width;
root->height = layout_box.height;
if (root->fullscreen_global) {
struct sway_container *fs = root->fullscreen_global;

Some files were not shown because too many files have changed in this diff Show more