diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 328625a5d..ddad4b1cc 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -12,7 +12,6 @@ packages: - meson - pango-dev - pixman-dev - - scdoc - wayland-dev - wayland-protocols - xcb-util-image-dev @@ -20,7 +19,12 @@ packages: sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots + - https://git.sr.ht/~sircmpwn/scdoc tasks: + - scdoc: | + cd scdoc + make PREFIX=/usr + sudo make install PREFIX=/usr - wlroots: | cd wlroots meson --prefix=/usr build -Drootston=false -Dexamples=false @@ -28,7 +32,7 @@ tasks: sudo ninja -C build install - setup: | cd sway - meson build + meson build -Dauto_features=enabled -Dtray=disabled - build: | cd sway ninja -C build diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index c8f116e00..983458496 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -8,7 +8,6 @@ packages: - libxkbcommon - meson - pango - - scdoc - wayland - wayland-protocols - xcb-util-image @@ -16,7 +15,12 @@ packages: sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots + - https://git.sr.ht/~sircmpwn/scdoc tasks: + - scdoc: | + cd scdoc + make PREFIX=/usr + sudo make install PREFIX=/usr - wlroots: | cd wlroots meson --prefix=/usr build -Drootston=false -Dexamples=false @@ -24,7 +28,7 @@ tasks: sudo ninja -C build install - setup: | cd sway - meson build + meson build -Dauto_features=enabled - build: | cd sway ninja -C build diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 8fa760257..91a3817d1 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -8,7 +8,6 @@ packages: - graphics/gdk-pixbuf2 - graphics/wayland - graphics/wayland-protocols -- textproc/scdoc - x11-toolkits/pango - x11/libxcb - x11/libxkbcommon @@ -22,10 +21,17 @@ packages: - x11/libX11 - x11/pixman - x11/xcb-util-wm +# scdoc dependencies +- devel/gmake sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots +- https://git.sr.ht/~sircmpwn/scdoc tasks: +- scdoc: | + cd scdoc + gmake PREFIX=/usr/local + sudo gmake install PREFIX=/usr/local - fixup_epoll: | cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc prefix=/usr/local diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..ae532f391 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +patreon: sircmpwn +custom: https://drewdevault.com/donate diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 7caa5d1e6..6014697e3 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -5,6 +5,8 @@ If you are using the nvidia proprietary driver for any reason, you have two choi If `lsmod | grep nvidia | wc -l` shows anything other than zero, your bug report is not welcome here. +Additionally, problems with Firefox are almost certainly Firefox bugs, not sway bugs. Start by submitting your issue to the Firefox bugzilla and come back here only after they confirm otherwise. + Otherwise, please include the following four components in your bug report: sway version, debug log, configuration (if applicable), and an explanation of steps taken to reproduce the issue. If sway crashes, also include a stack trace. Obtain your version like so: diff --git a/README.bg.md b/README.bg.md deleted file mode 100644 index dcdefc7e3..000000000 --- a/README.bg.md +++ /dev/null @@ -1,78 +0,0 @@ -# sway - -Sway е в процес на разработка, съвместим с i3, -[Wayland](http://wayland.freedesktop.org/) композитор. Прочетете -[FAQ](https://github.com/swaywm/sway/wiki). Присъединете се в [IRC -канала](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на -irc.freenode.net). - -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Ако желаете, може да дарите на [Patreon страницата на автора](https://patreon.com/sircmpwn), което ще помогне за цялостното здраве и развитие на проекта. - -## Помощ - -Ако имате нужда от помощ - влезте в IRC канала. Ако не ви отговорят в IRC или желаете кореспонденция по и-мейл - [fokditkak](mailto:martin.kalchev@mail.ru) -може да отговаря на основни въпроси на български език. - -## Подпис - -Версии подписани с ключ [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) -и публикувани в [GitHub](https://github.com/swaywm/sway/releases). - -## Инсталация - -### От пакети - -Sway съществува в репотата (хранилищата) на много дистрибуции. Пробвайте да го инсталирате със съответния пакетен мениджър на вашата дистрибуция. -В случай, че имате проблем погледнете [тази страница](https://github.com/swaywm/sway/wiki/Unsupported-packages) за помощ. - -Ако желаете да пакетирате Sway за вашата дистрибуция влезте в IRC канала -или пратете и-мейл на [sir@cmpwn.com](mailto:sir@cmpwn.com) за съвет как да го направите. - -### Компилиране от сорс-код - -Инсталирайте следните пакети: - -* meson -* [wlc](https://github.com/Cloudef/wlc) -* wayland -* xwayland -* libinput >= 1.6.0 -* libcap -* pcre -* json-c >= 0.13 -* pango -* cairo -* gdk-pixbuf2 * -* dbus >= 1.10 ** -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (Нужен за man страници) -* git - -_\*Нужен само за swaybar, swaybg_ - -_\*\*Нужен само за tray_ - -Изпълнете следните команди: - - meson build - ninja -C build - sudo ninja -C build install - -Ако имате logind: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - -Иначе: - - sudo chmod a+s /usr/local/bin/sway - -## Настройка - -Ако имте съшествуващa конфигурация от i3, просто я копирайте в `/.config/sway/config` и би трябвало безпроблемно да работи. -В противен случай, поставете примерната конфигурация, която би трябвало да се намиреа в `/etc/sway/config`. -Напишете `man 5 sway` за повече информация. - -## Пускане - -Напишете 'sway' в терминала (TTY). Някои дисплей мениджъри (display managers) може и да работят, но като цяло не са поддържани от Sway. diff --git a/README.de.md b/README.de.md index e8198013a..1abf2f4a6 100644 --- a/README.de.md +++ b/README.de.md @@ -1,84 +1,51 @@ -# sway +# Sway +Sway ist ein [i3](https://i3wm.org/)-kompatibler [Wayland](http://wayland.freedesktop.org/)-Compositor. Lies die [FAQ](https://github.com/swaywm/sway/wiki). Tritt dem [IRC Channel](http://webchat.freenode.net/?channels=sway&uio=d4) bei (#sway on irc.freenode.net; Englisch). -Der Fortschritt dieser Übersetzung kann [hier](https://github.com/swaywm/sway/issues/1318) -eingesehen werden. +Falls du die Entwicklung von Sway unterstützen möchtest, kannst du das auf [SirCmpwn's Patreon Seite](https://patreon.com/sircmpwn) machen. -Sway ist ein i3-kompatibler -[Wayland](http://wayland.freedesktop.org/)-Kompositor. Lies die -[FAQ](https://github.com/swaywm/sway/wiki#faq). Tritt dem -[IRC-Channel](http://webchat.freenode.net/?channels=sway&uio=d4) bei (#sway in -irc.freenode.net). - -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Falls du die Entwicklung von Sway unterstützen möchtest, kannst du das auf der -[Patreon-Seite](https://patreon.com/sircmpwn) tun, oder indem du zu -[Entwicklungsprämien](https://github.com/swaywm/sway/issues/986) -bestimmter Features beiträgst. Jeder ist dazu eingeladen, eine Prämie in Anspruch -zu nehmen oder für gewünschte Features bereitzustellen. Patreon ist eher dafür -gedacht, Sways Wartung und das Projekt generell zu unterstützen. - -## Deutscher Support - -refacto(UTC+2) bietet Support im IRC (unter dem Namen azarus) und auf Github an. -ParadoxSpiral(UTC+2) bietet Support im IRC und auf Github an. - -## Releasesignaturen - -Neue Versionen werden mit -[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) -signiert und [auf Github](https://github.com/swaywm/sway/releases) veröffentlicht. +## Signaturen +Jedes Release wird mit dem PGP-Schlüssel [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) signiert und auf GitHub veröffentlicht. ## Installation +### Mit der Paketverwaltung +Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Das Paket sollte "sway" heißen. Falls es kein solches Paket gibt, kannst du im [Wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) (Englisch) nach mehr Informationen bezüglich deiner Distribution suchen. -### Als Paket +Falls du sway für deine eigene Distribution als Paket bereitstellen möchtest, solltest du die Entwickler per IRC oder E-Mail (sir@cmpwn.com) kontaktieren. -Sway ist in vielen Distributionen verfügbar: versuche einfach, das „sway“-Paket -zu installieren. Falls es nicht vorhanden ist, schaue dir -[diese Wikiseite](https://github.com/swaywm/sway/wiki/Unsupported-packages) für -distributionsspezifische Installationsinformationen an. +### Quellcode selbst kompilieren -Wenn du Interesse hast, Sway für deine Distribution als Paket bereitzustellen, -schaue im IRC-Channel vorbei oder schreibe eine E‑Mail an sir@cmpwn.com (nur englischsprachig). +sway benötigt die folgenden Pakete: -### Kompilieren des Quellcodes - -Abhängigkeiten: - -* meson -* [wlc](https://github.com/Cloudef/wlc) +* meson\* +* [wlroots](https://github.com/swaywm/wlroots) * wayland -* xwayland -* libinput >= 1.6.0 -* libcap +* wayland-protocols\* * pcre -* json-c >= 0.13 +* json-c * pango * cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (erforderlich für man pages) -* git +* gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc)\* (Optional, wird für die Dokumentation (Man Pages) benötigt) +* git\* -_\*Nur erforderlich für swaybar, swaybg_ +_\*Werden nur während des Kompilierens benötigt_ -Führe diese Befehle aus: +Führe die folgenden Befehle aus meson build ninja -C build sudo ninja -C build install -In Systemen ohne logind musst du `sway` das suid-Flag geben: +Falls dein System nicht logind benutzt, musst du sway noch die passenden Berechtigungen geben: sudo chmod a+s /usr/local/bin/sway +Sway läuft nur in der Startphase mit Root-Rechten. + ## Konfiguration -Wenn du schon i3 benutzt, kopiere einfach deine i3 Konfiguration nach -`~/.config/sway/config`. Falls nicht, kannst du die Beispielkonfiguration -benutzen. Die befindet sich normalerweise unter `/etc/sway/config`. -Um mehr Informationen über die Konfiguration zu erhalten, führe `man 5 sway` aus. +Falls du von i3 migrierst, kannst du deine Konfigurationsdatei nach `~/.config/sway/config` kopieren, und die Einstellungen sollten ohne Weiteres funktionieren. Ansonsten kannst du die Beispielkonfiguration, die normalerweise in `/etc/sway/config` liegt, nach `~/.config/sway/config` kopieren. Die Dokumentation zur Konfigurationsdatei findest du in `man 5 sway`. -## Verwendung - -Führe `sway` von einem TTY aus. Manche Displaymanager könnten funktionieren, werden aber -nicht von Sway unterstützt (gdm scheint relativ gut zu funktionieren). +## Sway Starten +Sway kann einfach mit dem Befehl `sway` vom TTY gestartet werden. +Display-Manager werden nicht offiziell unterstützt. Es gibt aber durchaus einige, die mit Sway funktionieren. (z.B. gdm) diff --git a/README.el.md b/README.el.md deleted file mode 100644 index bc2360ca7..000000000 --- a/README.el.md +++ /dev/null @@ -1,73 +0,0 @@ -# sway - -Sway είναι ένας **υπό ανάπτυξη** [Wayland](http://wayland.freedesktop.org/) διαχειριστής παραθύρων συμβατός με τον αντίστοιχο διαχειριστή παραθύρων i3 για τον X11. -Διαβάστε τις [Συνήθεις Ερωτήσεις](https://github.com/swaywm/sway/wiki). Συνδεθείτε στο [κανάλι μας στο IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway στο -irc.freenode.net). - -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - - -### Η ελληνική μετάφραση ενδέχεται να είναι ελλειπής! - -Η τεκμηρίωση του Sway ξεκινάει πάντα από τα Αγγλικά και στη συνέχεια μεταφράζεται, γι' αυτό ενδέχεται τα ελληνικά κείμενα να μην είναι πάντα διαθέσιμα ή ενημερωμένα. -Μπορείτε πάντα να υποδεικνύετε σφάλματα και να κάνετε ερωτήσεις σχετικά με τις ελληνικές μεταφράσεις στο [IRC](http://webchat.freenode.net/?channels=sway&uio=d4). -To username μου στο Freenode είναι kon14 και θα με βρείτε στο IRC σε ώρες GMT+2. -Δείτε [εδώ](https://github.com/swaywm/sway/issues/1318) πως μπορείτε και οι ίδιοι να βοηθήσετε στη μετάφραση του Sway. - -Αν θέλετε να υποστηρίξετε την ανάπτυξη του Sway, μπορείτε να συμβάλετε στη [σελίδα Patreon του SirCmpwn](https://patreon.com/sircmpwn) -ή να επιδοτήσετε τις [αμοιβές](https://github.com/swaywm/sway/issues/986) για υλοποίηση συγκεκριμένων δυνατοτήτων. -Ο καθένας μπορεί να διεκδικήσει μια αμοιβή και μπορείτε να προσθέσετε μια αμοιβή για οποιαδήποτε δυνατότητα θέλετε. -Προτιμήστε το Patreon αν θέλετε να υποστήριξετε την συνολική ανάπτυξη και διατήρηση του Sway. - -## Υπογραφές Έκδοσης - -Οι εκδόσεις υπογράφονται ως [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) και δημοσιεύονται στο [GitHub](https://github.com/swaywm/sway/releases). - -## Εγκατάσταση - -### Από Πακέτα - -Ο Sway είναι διαθέσιμος για εγκατάσταση μέσω του διαχειριστή πακέτων σε διάφορες διανομές. -Δοκιμάστε να εγκαταστήσετε το πακέτο ονομαζόμενο ως "sway" για τη δική σας. -Αν δεν είναι διαθέσιμο, μεταβείτε στη [σελίδα τεκμηρίωσης](https://github.com/swaywm/sway/wiki/Unsupported-packages) για πληροφορίες σχετικά με την εγκατάσταση για τη διανομή σας. - -Αν ενδιαφέρεστε να δημιουργήσετε ένα πακέτο του Sway για τη διανομή σας, περάστε απο το κανάλι μας στο IRC ή στείλτε ένα email, στα **Αγγλικά**, στο sir@cmpwn.com για συμβουλές. - -### Compile από Πηγαίο Κώδικα - -Εγκατάσταση εξαρτήσεων: - -* meson -* [wlc](https://github.com/Cloudef/wlc) -* wayland -* xwayland -* libinput >= 1.6.0 -* libcap -* pcre -* json-c >= 0.13 -* pango -* cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) -* git - -_\*Απαιτείται μόνο για swaybar, swaybg_ - -Εκτελέστε αυτές τις εντολές: - - meson build - ninja -C build - sudo ninja -C build install - -Σε συστήματα χωρίς logind, χρειάζεται να θέσετε το suid bit στο εκτελέσιμο αρχείο: - - sudo chmod a+s /usr/local/bin/sway - -## Παραμετροποίηση - -Αν είστε ήδη χρήστης του i3, τότε απλά αντιγράψτε το αρχείο ρυθμίσεων σας στο `~/.config/sway/config` και θα είναι όλα έτοιμα για χρήση. -Διαφορετικά, αντιγράψτε το συνοδευόμενο δείγμα ρυθμίσεων, το οποίο θα βρείτε τυπικά στο `/etc/sway/config`, και μεταφέρετε το στην τοποθεσία `~/.config/sway/config`. Εκτελέστε `man 5 sway` για πληροφορίες σχετικά με την παραμετροποίηση των ρυθμίσεων σας. - -## Εκτέλεση - -Εκτελέστε `sway` απο ένα TTY. Μερικοί γραφικοί διαχειριστές σύνδεσης ενδέχεται να δουλεύουν, αλλά δεν υποστηρίζονται επίσημα (ο GDM "προτείνεται" ως λειτουργικός). diff --git a/README.es.md b/README.es.md index daa1fc093..2f2a1b069 100644 --- a/README.es.md +++ b/README.es.md @@ -1,9 +1,6 @@ # sway -[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - -[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con i3. +sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/). Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net). @@ -37,14 +34,12 @@ Instale las dependencias: * json-c * pango * cairo -* gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* gdk-pixbuf2 (optional: system tray) +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* * git \* _\*Compile-time dep_ -_\*\*opcional: necesario para swaybg_ - Desde su consola, ejecute las órdenes: meson build diff --git a/README.fr.md b/README.fr.md index ad627ba54..7068b3be0 100644 --- a/README.fr.md +++ b/README.fr.md @@ -1,19 +1,13 @@ # sway Sway est un compositeur [Wayland](http://wayland.freedesktop.org/) compatible -avec i3, **en cours de développement**. Lisez la +avec [i3](https://i3wm.org/), **en cours de développement**. Lisez la [FAQ](https://github.com/swaywm/sway/wiki). Rejoignez le [canal IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur irc.freenode.net). -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Si vous souhaitez soutenir le développement de Sway, vous pouvez contribuer à [ma page -Patreon](https://patreon.com/sircmpwn) ou aux [primes](https://github.com/swaywm/sway/issues/986) -pour des fonctionnalités spécifiques. -Tout le monde est invité à réclamer une prime et vous pouvez donner une prime pour n'importe quelle -fonctionnalité souhaitée. Patreon est plus utile pour supporter l'état général et la -maintenance de Sway. +Si vous souhaitez soutenir le développement de Sway, vous pouvez contribuer à [la page +Patreon de SirCmpwn](https://patreon.com/sircmpwn). ## Aide en français @@ -39,21 +33,19 @@ IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des consei Installez les dépendances : -* meson -* [wlc](https://github.com/Cloudef/wlc) +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) * wayland -* xwayland -* libinput >= 1.6.0 -* libcap +* wayland-protocols \* * pcre -* json-c >= 0.13 +* json-c * pango * cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (requis pour les pages man) -* git +* gdk-pixbuf2 (optionnel: system tray) +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optionnel: requis pour les pages man) \* +* git \* -_\*Uniquement requis pour swaybar, swaybg_ +_\*Requis uniquement pour la compilation_ Exécutez ces commandes : @@ -65,6 +57,8 @@ Sur les systèmes sans logind, vous devez suid le binaire de sway : sudo chmod a+s /usr/local/bin/sway +Sway se débarassera des permissions *root* peu de temps après le démarrage. + ## Configuration Si vous utilisez déjà i3, copiez votre configuration i3 à `~/.config/sway/config` et @@ -72,10 +66,6 @@ cela va fonctionner. Sinon, copiez l'exemple de fichier de configuration à `~/.config/sway/config`. Il se trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour l'information sur la configuration. -Mes propres dotfiles sont disponibles [ici](https://git.sr.ht/~sircmpwn/dotfiles) si -vous voulez un peu d'inspiration. Je vous recommande aussi de consulter le -[wiki](https://github.com/swaywm/sway/wiki). - ## Exécution Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent fonctionner, diff --git a/README.it.md b/README.it.md deleted file mode 100644 index bfe099205..000000000 --- a/README.it.md +++ /dev/null @@ -1,79 +0,0 @@ -# sway - -Sway è un compositor [Wayland](http://wayland.freedesktop.org/) **in via di -sviluppo** compatibile con i3. Leggi le [FAQ (in -Inglese)](https://github.com/swaywm/sway/wiki). Unisciti al [canale IRC (in -Inglese)](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on -irc.freenode.net). - -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Se vuoi supportare lo sviluppo di Sway, puoi contribuire dalla -[pagina Patreon di SirCmpwn's](https://patreon.com/sircmpwn) o con dei -[premi](https://github.com/swaywm/sway/issues/986) per finanziare lo sviluppo -di funzionalità specifiche. -Chiunque è libero di reclamare un premio o crearne uno per qualsiasi funzionalità. -Patreon è più utile al supporto e alla manutenzione generale di Sway. - -## Supporto italiano -syknro offre supporto in Italiano su GitHub nel fuso orario UTC+2. -Questa traduzione non è ancora completa. [Clicca qui per maggiori informazioni](https://github.com/swaywm/sway/issues/1318) - -## Firme digitali - -Le release sono firmate con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) -e pubblicate [su GitHub](https://github.com/swaywm/sway/releases). - -## Installazione - -### Dai pacchetti - -Sway è disponibile in molte distribuzioni. Prova a installare il pacchetto "sway" per la tua. -Se non funziona, controlla [questa pagina (in Inglese)](https://github.com/swaywm/sway/wiki/Unsupported-packages) -per informazioni sull'installazione per le tue distribuzioni. - -Se vuoi creare un pacchetto per la tua distribuzione, passa dall'IRC o manda un email (in Inglese) -a sir@cmpwn.com. - -### Compilando il codice sorgente - -Installa queste dipendenze: - -* meson -* [wlc](https://github.com/Cloudef/wlc) -* wayland -* xwayland -* libinput >= 1.6.0 -* libcap -* pcre -* json-c >= 0.13 -* pango -* cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (rrichiesto per man pages) -* git - -_\*Richiesto solo per swaybar, swaybg_ - -Esegui questi comandi: - - meson build - ninja -C build - sudo ninja -C build install - -Per i sistemi senza logind, devi cambiare i permessi (suid): - - sudo chmod a+s /usr/local/bin/sway - -## Configurazione - -Se usi i3, copia la tua configurazione in `~/.config/sway/config` e -funzionerà direttamente. -Altrimenti copia in `~/.config/sway/config` la configurazione di esempio, -solitamente si trova in `/etc/sway/config`. -Esegui `man 5 sway` per informazioni sulla configurazione. - -## Esecuzione - -Esegui `sway` da un TTY. Qualche display manager potrebbe funzionare ma non sono -ufficialmente supportati da Sway (gdm è risaputo funzionare abbastanza bene). diff --git a/README.ja.md b/README.ja.md index 800daa62d..21528a9fd 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1,6 +1,6 @@ # sway -Swayはi3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。 +Swayは[i3](https://i3wm.org/)互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。 [FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 [IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。 @@ -36,14 +36,12 @@ Swayは沢山のディストリビューションで提供されています。" * json-c * pango * cairo -* gdk-pixbuf2 \*\* +* gdk-pixbuf2 (システムイコンで必要です) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) \* * git \* _\*コンパイルの時_ -_\*\*オプション: swaybgでのみ必要です_ - 次のコマンドを実行してください: meson build diff --git a/README.md b/README.md index 171d71e46..231fcf9ae 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # sway -[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Indonesian](https://github.com/swaywm/sway/blob/master/README.id.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - -[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) +[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - [Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [中文-简体](https://github.com/swaywm/sway/blob/master/README.zh-CN.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Indonesian](https://github.com/swaywm/sway/blob/master/README.id.md#sway--) + +sway is an [i3](https://i3wm.org/)-compatible [Wayland](http://wayland.freedesktop.org/) compositor. -sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net). @@ -41,14 +40,12 @@ Install dependencies: * json-c * pango * cairo -* gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* gdk-pixbuf2 (optional: system tray) +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* * git \* _\*Compile-time dep_ -_\*\*optional: required for swaybg_ - Run these commands: meson build diff --git a/README.pl.md b/README.pl.md index 843fae605..ddfd16766 100644 --- a/README.pl.md +++ b/README.pl.md @@ -1,10 +1,6 @@ # sway -[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - -[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - -sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z i3. +sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/). Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway na irc.freenode.net). @@ -38,14 +34,12 @@ Zainstaluj zależności: * json-c * pango * cairo -* gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (opcjonalnie: strony pomocy man) \* +* gdk-pixbuf2 (opcjonalnie: system tray) +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opcjonalnie: strony pomocy man) \* * git \* _\*zależności kompilacji_ -_\*\*opcjonalnie: wymagane dla swaybg_ - Wykonaj następujące polecenia: meson build diff --git a/README.pt.md b/README.pt.md deleted file mode 100644 index 0f2a3cd49..000000000 --- a/README.pt.md +++ /dev/null @@ -1,91 +0,0 @@ -# sway - -Sway é um compositor [Wayland](http://wayland.freedesktop.org/) compatível com o -i3. Leia o [FAQ](https://github.com/swaywm/sway/wiki). Participe do [canal -IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway no -irc.freenode.net). - -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Se você deseja apoiar o desenvolvimento do Sway, você pode contribuir com o -SirCmpwn em sua [página no Patreon](https://patreon.com/sircmpwn) ou você -pode colaborar com [premiações](https://github.com/swaywm/sway/issues/986) -para recursos específicos. Qualquer um pode requerer uma premiação ao implementar -o recurso especificado, e você pode criar uma premiação para qualquer recurso que desejar. -O Patreon é melhor direcionado para a manutenção a longo prazo do Sway. - -## Ajuda em português - -No momento, o suporte em português no canal do IRC **não está ativo**. Em caso de problemas, -use as [*issues*](https://github.com/swaywm/sway/issues/) do Github (*em inglês*). - -A tradução para português é um *trabalho em progresso*, no momento. Caso encontre algum erro -ou queira colaborar com a tradução, visite -[essa *issue*](https://github.com/swaywm/sway/issues/1318) para mais informações e não -exite em enviar quaisquer correções necessárias. - -## Assinaturas dos *Releases* - -*Releases* são assinadas com a chave -[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) -e publicadas [no GitHub](https://github.com/swaywm/sway/releases). - -## Instalação - -### A partir de pacotes - -Sway está disponível em várias distribuições. Verifique se o pacote "sway" está -disponível a partir do gerenciador de pacotes da sua distribuição. Caso não esteja, -procure por informações sobre como instalar o Sway na sua distribuição -[aqui](https://github.com/swaywm/sway/wiki/Unsupported-packages). - -Se você está interessado em manter um pacote do Sway para a sua distribuição, -visite o canal no IRC ou mande um email para sir@cmpwn.com (*em inglês*). - -### A partir do código-fonte - -Antes de iniciar a compilação, instale as dependências: - -* meson -* [wlc](https://github.com/Cloudef/wlc) -* wayland -* xwayland -* libinput >= 1.6.0 -* libcap -* pcre -* json-c >= 0.13 -* pango -* cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (man pages) -* git - -_\*Dependência apenas de swaybar, swaybg_ - -Para compilar, execute estes comandos: - - meson build - ninja -C build - sudo ninja -C build install - -Em sistemas com logind, configure as seguintes capacidades para o arquivo binário: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - -Em sistemas sem logind, ative a *flag* de *suid* do arquivo binário: - - sudo chmod a+s /usr/local/bin/sway - -## Configuração - -Se você já usa o i3, copie o arquivo de configuração do i3 para `~/.config/sway/config`; -o Sway lerá o arquivo normalmente. Senão, copie o arquivo de configuração de exemplo -para `~/.config/sway/config`. É comum esse arquivo estar localizado em -`/etc/sway/config`. Veja `man 5 sway` para informações sobre configuração. - -## Executando - -Execute `sway` a partir de um terminal do Linux. Alguns gerenciadores de *display* -podem funcionar, porém o Sway não procura manter compatibilidade com esses (segundo -relatos, o gdm funciona bem com o Sway). - diff --git a/README.ru.md b/README.ru.md deleted file mode 100644 index c66976e58..000000000 --- a/README.ru.md +++ /dev/null @@ -1,83 +0,0 @@ -# sway - -Sway на данный момент **(в разработке)** i3-совместимый -[Wayland](http://wayland.freedesktop.org/) композитор. Прочитайте -[FAQ](https://github.com/swaywm/sway/wiki). Присоединяйтесь к [IRC -каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на -irc.freenode.net). - -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -При желании поддержать разработку Sway вы можете пожертвовать [автору -на его Patreon странице](https://patreon.com/sircmpwn) или взяться -за разработку определённых целей в обмен на [награду](https://github.com/swaywm/sway/issues/986). - -Вы также можете объявить свою награду за определённую цель и больше всего для этого подходит Patreon. - -## Помощь - -DarkReef оказывает поддержку на русском языке в IRC канале и на GitHub в часовом поясе UTC +05:00. -Если у вас есть желание помочь с переводом на русский язык, то, пожалуйста, ознакомьтесь с [подсказками для переводчиков](https://github.com/swaywm/sway/issues/1318). На этой же странице можно узнать [статус перевода](https://github.com/swaywm/sway/issues/1318#issuecomment-326913020). - -## Подпись версий - -Версии подписаны ключом [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) -и опубликованы [на GitHub'е](https://github.com/swaywm/sway/releases). - -## Установка - -### Из пакета - -Sway доступен во многих дистрибутивах и находится в официальных репозиториях. Попробуйте установить "sway" через ваш пакетный менеджер. -В случае, если это не представляется возможным, то обратитесь к [этой странице](https://github.com/swaywm/sway/wiki/Unsupported-packages) -для получения инструкций по установке для вашего дистрибутива. - -Если вы заинтересованы в создании пакета "sway" в вашем дистрибутиве, то сообщите об этом в IRC -канале или отправьте письмо [sir@cmpwn.com](mailto:sir@cmpwn.com). - -### Сборка из исходников - -Установите следующие пакеты: - -* meson -* [wlc](https://github.com/Cloudef/wlc) -* wayland -* xwayland -* libinput >= 1.6.0 -* libcap -* pcre -* json-c >= 0.13 -* pango -* cairo -* gdk-pixbuf2 * -* dbus >= 1.10 ** -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) -* git - -_\*Требуется только для swaybar, swaybg_ - -_\*\*Требуется только для tray_ - -Выполните следующие команды: - - meson build - ninja -C build - sudo ninja -C build install - -Если у вас logind: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - -Иначе: - - sudo chmod a+s /usr/local/bin/sway - -## Настройка - -Если вы уже используете i3, тогда просто скопируйте ваш конфиг в `~/.config/sway/config`. -В любом другом случае, скопируйте `/etc/sway/config` в `~/.config/sway/config`. -Для более детальной информации о настройке: `man 5 sway`. - -## Запуск - -Выполните 'sway' в терминале. **Некоторые** менеджеры сессий могут работать, но не поддерживаются sway (к примеру, gdm работает со sway без проблем). diff --git a/README.uk.md b/README.uk.md index 4c378a89f..1c0065667 100644 --- a/README.uk.md +++ b/README.uk.md @@ -1,20 +1,12 @@ # sway -**Sway** це сумісний з i3 композитор [Wayland](http://wayland.freedesktop.org/) -(**у стані розробки**). Ознайомтесь з -[ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в +Sway це сумісний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). +Ознайомтесь з [ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на irc.freenode.net). -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Якщо ви хочете підтримати розробку Sway, ви можете зробити свій внесок у -[SirCmpwn'ову сторінку Patreon](https://patreon.com/sircmpwn) або до -[фонду винагород](https://github.com/swaywm/sway/issues/986) за реалізацію -певного функціоналу. -Кожен може виставити винагороду за реалізацію довільної функції -(і, відповідно, забрати її собі, виконавши це завдання); -кошти від сторінки Patreon підтримують загальну розробку та підтримку Sway. +Якщо ви маєте бажання підтримати розробку sway, ви можете зробити свій внесок на сторінці +[SirCmpwn у Patreon](https://patreon.com/sircmpwn). ## Підтримка українською мовою @@ -39,28 +31,26 @@ Sway доступний у багатьох дистрибутивах Linux (а для інформації щодо встановлення на вашому дистрибутиві. Якщо ви готові та зацікавлені запакувати і підтримувати Sway у вашому -дистрибутиві, будемо раді вас бачити у нашому каналі IRC. Ви також можете -спитати порад за адресою sir@cmpwn.com. +дистрибутиві, звертайтесь за порадами до нашого каналу в IRC або +пишіть на електронну пошту [sir@cmpwn.com](mailto:sir@cmpwn.com). ### З вихідного коду Встановіть залежності: -* meson -* [wlc](https://github.com/Cloudef/wlc) +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) * wayland -* xwayland -* libinput >= 1.6.0 -* libcap +* wayland-protocols \* * pcre -* json-c >= 0.13 +* json-c * pango * cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) -* git +* gdk-pixbuf2 (optional: system tray) +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (необов'язково, необхідно для сторінок man) \* +* git \* -_\*Лише для swaybar, swaybg_ +_\*Лише для компіляції_ Виконайте ці команди: @@ -68,15 +58,12 @@ _\*Лише для swaybar, swaybg_ ninja -C build sudo ninja -C build install -На системах **з** logind, варто встановити декілька можливостей (caps) -на виконуваний файл sway: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - -На системах **без** logind, необхідно встановити біт SUID на виконуваний файл sway: +На системах без logind, необхідно встановити біт SUID на виконуваний файл sway: sudo chmod a+s /usr/local/bin/sway +Sway втратить права доступу root незабаром після запуску. + ## Налаштування Якщо ви вже використовуєте i3, скопіюйте свій файл налаштувань diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 000000000..99dc7d835 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,66 @@ +# sway + +sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. +阅读 [FAQ](https://github.com/swaywm/sway/wiki). 加入 [IRC +频道](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on +irc.freenode.net). + +如果你想要支持 sway 的发展, 请到 [SirCmpwn's +Patreon page](https://patreon.com/sircmpwn)贡献. + +## 发布签名 + +发布是以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 签名 +并发布在 [GitHub](https://github.com/swaywm/sway/releases). + +## 安装 + +### 从软件包中 + +Sway 在很多发行版中可用. 尝试在你的发行版中安装 "sway" 包. +如果这不可用, 请到 [此 wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) +检查针对你的发行版关于安装的信息. + +如果你有兴趣给你的发行版打包 sway, 停下来到 IRC 频道或者发邮件至 sir@cmpwn.com 获取建议. + +### 从源代码编译 + +安装依赖: + +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) +* wayland +* wayland-protocols \* +* pcre +* json-c +* pango +* cairo +* gdk-pixbuf2 (可选的: system tray) +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (可选的: man pages) \* +* git \* + +_\*编译时依赖_ + +运行这些命令: + + meson build + ninja -C build + sudo ninja -C build install + +在没有 logind 的系统上, 你需要给 sway 二进制设置 suid: + + sudo chmod a+s /usr/local/bin/sway + +Sway 将会在启动后尽快丢掉 root 权限. + +## 配置 + +如果你已经在使用 i3, 接下来复制你的 i3 配置到 `~/.config/sway/config` +它可以直接工作. 或者, 复制样本配置文件到 +`~/.config/sway/config`. 它通常位于 `/etc/sway/config`. +运行 `man 5 sway` 获取关于配置的信息. + +## 运行 + +从 TTY 中运行 `sway` . 某些显示管理器可能会工作但并不被 sway 支持 +(已知的 gdm 工作得非常好). diff --git a/client/meson.build b/client/meson.build index abe6f1eba..4484a38c7 100644 --- a/client/meson.build +++ b/client/meson.build @@ -5,7 +5,6 @@ lib_sway_client = static_library( ), dependencies: [ cairo, - gdk_pixbuf, pango, pangocairo, wayland_client diff --git a/common/background-image.c b/common/background-image.c index 4431b725e..de42e8e94 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -2,6 +2,9 @@ #include "background-image.h" #include "cairo.h" #include "log.h" +#if HAVE_GDK_PIXBUF +#include +#endif enum background_mode parse_background_mode(const char *mode) { if (strcmp(mode, "stretch") == 0) { @@ -21,6 +24,103 @@ enum background_mode parse_background_mode(const char *mode) { return BACKGROUND_MODE_INVALID; } +#if HAVE_GDK_PIXBUF +static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( + const GdkPixbuf *gdkbuf) { + int chan = gdk_pixbuf_get_n_channels(gdkbuf); + if (chan < 3) { + return NULL; + } + + const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); + if (!gdkpix) { + return NULL; + } + gint w = gdk_pixbuf_get_width(gdkbuf); + gint h = gdk_pixbuf_get_height(gdkbuf); + int stride = gdk_pixbuf_get_rowstride(gdkbuf); + + cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; + cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h); + cairo_surface_flush (cs); + if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) { + return NULL; + } + + int cstride = cairo_image_surface_get_stride(cs); + unsigned char * cpix = cairo_image_surface_get_data(cs); + + if (chan == 3) { + int i; + for (i = h; i; --i) { + const guint8 *gp = gdkpix; + unsigned char *cp = cpix; + const guint8* end = gp + 3*w; + while (gp < end) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + cp[0] = gp[2]; + cp[1] = gp[1]; + cp[2] = gp[0]; +#else + cp[1] = gp[0]; + cp[2] = gp[1]; + cp[3] = gp[2]; +#endif + gp += 3; + cp += 4; + } + gdkpix += stride; + cpix += cstride; + } + } else { + /* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255 + * (z/255) = z/256 * 256/255 = z/256 (1 + 1/255) + * = z/256 + (z/256)/255 = (z + z/255)/256 + * # recurse once + * = (z + (z + z/255)/256)/256 + * = (z + z/256 + z/256/255) / 256 + * # only use 16bit uint operations, loose some precision, + * # result is floored. + * -> (z + z>>8)>>8 + * # add 0x80/255 = 0.5 to convert floor to round + * => (z+0x80 + (z+0x80)>>8 ) >> 8 + * ------ + * tested as equal to lround(z/255.0) for uint z in [0..0xfe02] + */ +#define PREMUL_ALPHA(x,a,b,z) \ + G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \ + G_STMT_END + int i; + for (i = h; i; --i) { + const guint8 *gp = gdkpix; + unsigned char *cp = cpix; + const guint8* end = gp + 4*w; + guint z1, z2, z3; + while (gp < end) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + PREMUL_ALPHA(cp[0], gp[2], gp[3], z1); + PREMUL_ALPHA(cp[1], gp[1], gp[3], z2); + PREMUL_ALPHA(cp[2], gp[0], gp[3], z3); + cp[3] = gp[3]; +#else + PREMUL_ALPHA(cp[1], gp[0], gp[3], z1); + PREMUL_ALPHA(cp[2], gp[1], gp[3], z2); + PREMUL_ALPHA(cp[3], gp[2], gp[3], z3); + cp[0] = gp[3]; +#endif + gp += 4; + cp += 4; + } + gdkpix += stride; + cpix += cstride; + } +#undef PREMUL_ALPHA + } + cairo_surface_mark_dirty(cs); + return cs; +} +#endif // HAVE_GDK_PIXBUF + cairo_surface_t *load_background_image(const char *path) { cairo_surface_t *image; #if HAVE_GDK_PIXBUF diff --git a/common/cairo.c b/common/cairo.c index f2ad54c18..403dcf490 100644 --- a/common/cairo.c +++ b/common/cairo.c @@ -1,9 +1,6 @@ #include #include #include "cairo.h" -#if HAVE_GDK_PIXBUF -#include -#endif void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { cairo_set_source_rgba(cairo, @@ -45,99 +42,3 @@ cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, cairo_destroy(cairo); return new; } - -#if HAVE_GDK_PIXBUF -cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { - int chan = gdk_pixbuf_get_n_channels(gdkbuf); - if (chan < 3) { - return NULL; - } - - const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); - if (!gdkpix) { - return NULL; - } - gint w = gdk_pixbuf_get_width(gdkbuf); - gint h = gdk_pixbuf_get_height(gdkbuf); - int stride = gdk_pixbuf_get_rowstride(gdkbuf); - - cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; - cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h); - cairo_surface_flush (cs); - if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) { - return NULL; - } - - int cstride = cairo_image_surface_get_stride(cs); - unsigned char * cpix = cairo_image_surface_get_data(cs); - - if (chan == 3) { - int i; - for (i = h; i; --i) { - const guint8 *gp = gdkpix; - unsigned char *cp = cpix; - const guint8* end = gp + 3*w; - while (gp < end) { -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - cp[0] = gp[2]; - cp[1] = gp[1]; - cp[2] = gp[0]; -#else - cp[1] = gp[0]; - cp[2] = gp[1]; - cp[3] = gp[2]; -#endif - gp += 3; - cp += 4; - } - gdkpix += stride; - cpix += cstride; - } - } else { - /* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255 - * (z/255) = z/256 * 256/255 = z/256 (1 + 1/255) - * = z/256 + (z/256)/255 = (z + z/255)/256 - * # recurse once - * = (z + (z + z/255)/256)/256 - * = (z + z/256 + z/256/255) / 256 - * # only use 16bit uint operations, loose some precision, - * # result is floored. - * -> (z + z>>8)>>8 - * # add 0x80/255 = 0.5 to convert floor to round - * => (z+0x80 + (z+0x80)>>8 ) >> 8 - * ------ - * tested as equal to lround(z/255.0) for uint z in [0..0xfe02] - */ -#define PREMUL_ALPHA(x,a,b,z) \ - G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \ - G_STMT_END - int i; - for (i = h; i; --i) { - const guint8 *gp = gdkpix; - unsigned char *cp = cpix; - const guint8* end = gp + 4*w; - guint z1, z2, z3; - while (gp < end) { -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - PREMUL_ALPHA(cp[0], gp[2], gp[3], z1); - PREMUL_ALPHA(cp[1], gp[1], gp[3], z2); - PREMUL_ALPHA(cp[2], gp[0], gp[3], z3); - cp[3] = gp[3]; -#else - PREMUL_ALPHA(cp[1], gp[0], gp[3], z1); - PREMUL_ALPHA(cp[2], gp[1], gp[3], z2); - PREMUL_ALPHA(cp[3], gp[2], gp[3], z3); - cp[0] = gp[3]; -#endif - gp += 4; - cp += 4; - } - gdkpix += stride; - cpix += cstride; - } -#undef PREMUL_ALPHA - } - cairo_surface_mark_dirty(cs); - return cs; -} -#endif // HAVE_GDK_PIXBUF diff --git a/common/ipc-client.c b/common/ipc-client.c index 13e2dfa32..4750a2891 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -69,6 +69,14 @@ int ipc_open_socket(const char *socket_path) { return socketfd; } +bool ipc_set_recv_timeout(int socketfd, struct timeval tv) { + if (setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { + sway_log_errno(SWAY_ERROR, "Failed to set ipc recv timeout"); + return false; + } + return true; +} + struct ipc_response *ipc_recv_response(int socketfd) { char data[IPC_HEADER_SIZE]; uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic)); diff --git a/common/stringop.c b/common/stringop.c index 709be6842..dea152cce 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,13 +1,13 @@ #define _POSIX_C_SOURCE 200809L -#include +#include +#include #include +#include #include #include -#include -#include "stringop.h" -#include "log.h" -#include "string.h" #include "list.h" +#include "log.h" +#include "stringop.h" static const char whitespace[] = " \f\n\r\t\v"; @@ -78,12 +78,10 @@ int lenient_strcmp(char *a, char *b) { list_t *split_string(const char *str, const char *delims) { list_t *res = create_list(); char *copy = strdup(str); - char *token; - token = strtok(copy, delims); - while(token) { - token = strdup(token); - list_add(res, token); + char *token = strtok(copy, delims); + while (token) { + list_add(res, strdup(token)); token = strtok(NULL, delims); } free(copy); @@ -150,29 +148,6 @@ void free_argv(int argc, char **argv) { free(argv); } -char *code_strstr(const char *haystack, const char *needle) { - /* TODO */ - return strstr(haystack, needle); -} - -char *code_strchr(const char *str, char delimiter) { - int in_string = 0, in_character = 0; - int i = 0; - while (str[i] != '\0') { - if (str[i] == '"' && !in_character) { - in_string = !in_string; - } else if (str[i] == '\'' && !in_string) { - in_character = !in_character; - } else if (!in_character && !in_string) { - if (str[i] == delimiter) { - return (char *)str + i; - } - } - ++i; - } - return NULL; -} - int unescape_string(char *string) { /* TODO: More C string escapes */ int len = strlen(string); @@ -276,84 +251,6 @@ char *join_args(char **argv, int argc) { return res; } -static bool has_whitespace(const char *str) { - while (*str) { - if (isspace(*str)) { - return true; - } - ++str; - } - return false; -} - -/** - * Add quotes around any argv with whitespaces. - */ -void add_quotes(char **argv, int argc) { - int i; - for (i = 0; i < argc; ++i) { - if (has_whitespace(argv[i])) { - int len = strlen(argv[i]) + 3; - char *tmp = argv[i]; - argv[i] = malloc(len * sizeof(char)); - snprintf(argv[i], len, "\"%s\"", tmp); - free(tmp); - } - } -} - -/* - * Join a list of strings, adding separator in between. Separator can be NULL. - */ -char *join_list(list_t *list, char *separator) { - if (!sway_assert(list != NULL, "list != NULL") || list->length == 0) { - return NULL; - } - - size_t len = 1; // NULL terminator - size_t sep_len = 0; - if (separator != NULL) { - sep_len = strlen(separator); - len += (list->length - 1) * sep_len; - } - - for (int i = 0; i < list->length; i++) { - len += strlen(list->items[i]); - } - - char *res = malloc(len); - - char *p = res + strlen(list->items[0]); - strcpy(res, list->items[0]); - - for (int i = 1; i < list->length; i++) { - if (sep_len) { - memcpy(p, separator, sep_len); - p += sep_len; - } - strcpy(p, list->items[i]); - p += strlen(list->items[i]); - } - - *p = '\0'; - - return res; -} - -char *cmdsep(char **stringp, const char *delim) { - // skip over leading delims - char *head = *stringp + strspn(*stringp, delim); - // Find end token - char *tail = *stringp += strcspn(*stringp, delim); - // Set stringp to beginning of next token - *stringp += strspn(*stringp, delim); - // Set stringp to null if last token - if (!**stringp) *stringp = NULL; - // Nullify end of first token - *tail = 0; - return head; -} - char *argsep(char **stringp, const char *delim) { char *start = *stringp; char *end = start; @@ -369,13 +266,13 @@ char *argsep(char **stringp, const char *delim) { escaped = !escaped; } else if (*end == '\0') { *stringp = NULL; - goto found; + break; } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { if (end - start) { *(end++) = 0; *stringp = end + strspn(end, delim);; if (!**stringp) *stringp = NULL; - goto found; + break; } else { ++start; end = start; @@ -386,20 +283,5 @@ char *argsep(char **stringp, const char *delim) { } ++end; } - found: return start; } - -const char *strcasestr(const char *haystack, const char *needle) { - size_t needle_len = strlen(needle); - const char *pos = haystack; - const char *end = pos + strlen(haystack) - needle_len; - - while (pos <= end) { - if (strncasecmp(pos, needle, needle_len) == 0) { - return pos; - } - ++pos; - } - return NULL; -} diff --git a/common/util.c b/common/util.c index edbbf3f77..3a807edba 100644 --- a/common/util.c +++ b/common/util.c @@ -1,9 +1,11 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include #include +#include #include "log.h" #include "util.h" @@ -54,3 +56,41 @@ float parse_float(const char *value) { } return flt; } + + +const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) { + switch (subpixel) { + case WL_OUTPUT_SUBPIXEL_UNKNOWN: + return "unknown"; + case WL_OUTPUT_SUBPIXEL_NONE: + return "none"; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: + return "rgb"; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: + return "bgr"; + case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: + return "vrgb"; + case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: + return "vbgr"; + } + sway_assert(false, "Unknown value for wl_output_subpixel."); + return NULL; +} + +bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index 20092bdc4..a333c9124 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -17,10 +17,13 @@ _swaymsg() 'get_binding_modes' 'get_config' 'send_tick' + 'subscribe' ) short=( -h + -m + -p -q -r -s @@ -30,6 +33,8 @@ _swaymsg() long=( --help + --monitor + --pretty --quiet --raw --socket diff --git a/completions/fish/sway.fish b/completions/fish/sway.fish index 31165ef4f..d488d49ab 100644 --- a/completions/fish/sway.fish +++ b/completions/fish/sway.fish @@ -1,5 +1,6 @@ # sway(1) completion +complete -f -c sway complete -c sway -s h -l help --description "Show help message and quit." complete -c sway -s c -l config --description "Specifies a config file." complete -c sway -s C -l validate --description "Check the validity of the config file, then exit." diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index fd577413f..25085f9a7 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -1,10 +1,13 @@ # swaymsg(1) completion +complete -f -c swaymsg complete -c swaymsg -s h -l help --description "Show help message and quit." +complete -c swaymsg -s m -l monitor --description "Monitor subscribed events until killed." +complete -c swaymsg -s p -l pretty --description "Use pretty output even when not using a tty." complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." -complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." complete -c swaymsg -s r -l raw --description "Use raw output even if using tty." complete -c swaymsg -s s -l socket -r --description "Use the specified socket path. Otherwise, swaymsg will ask where the socket is (which is the value of $SWAYSOCK, then of $I3SOCK)." +complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." complete -c swaymsg -s t -l type -fr --description "Specify the type of IPC message." complete -c swaymsg -s t -l type -fra 'get_workspaces' --description "Gets a JSON-encoded list of workspaces and their status." @@ -18,3 +21,4 @@ complete -c swaymsg -s t -l type -fra 'get_binding_modes' --description "Gets a complete -c swaymsg -s t -l type -fra 'get_config' --description "Gets a JSON-encoded copy of the current configuration." complete -c swaymsg -s t -l type -fra 'get_seats' --description "Gets a JSON-encoded list of all seats, its properties and all assigned devices." complete -c swaymsg -s t -l type -fra 'send_tick' --description "Sends a tick event to all subscribed clients." +complete -c swaymsg -s t -l type -fra 'subscribe' --description "Subscribe to a list of event types." diff --git a/completions/fish/swaynag.fish b/completions/fish/swaynag.fish index 784d7fade..7015d5e34 100644 --- a/completions/fish/swaynag.fish +++ b/completions/fish/swaynag.fish @@ -1,29 +1,30 @@ # swaynag -complete -c swaynag -s C -l config --description 'The config file to use. Default: $HOME/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config, and SYSCONFDIR/swaylock/config.' +complete -f -c swaynag +complete -c swaynag -s C -l config -r --description 'The config file to use. Default: $HOME/.swaynag/config, $XDG_CONFIG_HOME/swaynag/config, and SYSCONFDIR/swaynag/config.' complete -c swaynag -s d -l debug --description 'Enable debugging.' -complete -c swaynag -s e -l edge --description 'Set the edge to use: top or bottom' -complete -c swaynag -s f -l font --description 'Set the font to use.' +complete -c swaynag -s e -l edge -fr --description 'Set the edge to use: top or bottom' +complete -c swaynag -s f -l font -r --description 'Set the font to use.' complete -c swaynag -s h -l help --description 'Show help message and quit.' -complete -c swaynag -s b -l button --description 'Create a button with a text and an action which is executed when pressed. Multiple buttons can be defined by providing the flag multiple times.' +complete -c swaynag -s b -l button -fr --description 'Create a button with a text and an action which is executed when pressed. Multiple buttons can be defined by providing the flag multiple times.' complete -c swaynag -s l -l detailed-message --description 'Read a detailed message from stdin. A button to toggle details will be added. Details are shown in a scrollable multi-line text area.' -complete -c swaynag -s L -l detailed-button --description 'Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is "Toggle details".' -complete -c swaynag -s m -l message --description 'Set the message text.' -complete -c swaynag -s o -l output --description 'Set the output to use.' -complete -c swaynag -s s -l dismiss-button --description 'Sets the text for the dismiss nagbar button. The default is "X".' -complete -c swaynag -s t -l type --description 'Set the message type. Two types are created by default "error" and "warning". Custom types can be defined in the config file.' +complete -c swaynag -s L -l detailed-button -fr --description 'Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is "Toggle details".' +complete -c swaynag -s m -l message -fr --description 'Set the message text.' +complete -c swaynag -s o -l output -fr --description 'Set the output to use.' +complete -c swaynag -s s -l dismiss-button -fr --description 'Sets the text for the dismiss nagbar button. The default is "X".' +complete -c swaynag -s t -l type -fr --description 'Set the message type. Two types are created by default "error" and "warning". Custom types can be defined in the config file.' complete -c swaynag -s v -l version --description 'Show the version number and quit.' # Appearance -complete -c swaynag -l background --description 'Set the color of the background.' -complete -c swaynag -l border --description 'Set the color of the border.' -complete -c swaynag -l border-bottom --description 'Set the color of the bottom border.' -complete -c swaynag -l button-background --description 'Set the color for the background for buttons.' -complete -c swaynag -l text --description 'Set the text color.' -complete -c swaynag -l border-bottom-size --description 'Set the thickness of the bottom border.' -complete -c swaynag -l message-padding --description 'Set the padding for the message.' -complete -c swaynag -l details-border-size --description 'Set the thickness for the details border.' -complete -c swaynag -l button-border-size --description 'Set the thickness for the button border.' -complete -c swaynag -l button-gap --description 'Set the size of the gap between buttons.' -complete -c swaynag -l button-dismiss-gap --description 'Set the size of the gap between the dismiss button and another button.' -complete -c swaynag -l button-margin-right --description 'Set the margin from the right of the dismiss button to edge.' -complete -c swaynag -l button-padding --description 'Set the padding for the button text.' +complete -c swaynag -l background -fr --description 'Set the color of the background.' +complete -c swaynag -l border -fr --description 'Set the color of the border.' +complete -c swaynag -l border-bottom -fr --description 'Set the color of the bottom border.' +complete -c swaynag -l button-background -fr --description 'Set the color for the background for buttons.' +complete -c swaynag -l text -fr --description 'Set the text color.' +complete -c swaynag -l border-bottom-size -fr --description 'Set the thickness of the bottom border.' +complete -c swaynag -l message-padding -fr --description 'Set the padding for the message.' +complete -c swaynag -l details-border-size -fr --description 'Set the thickness for the details border.' +complete -c swaynag -l button-border-size -fr --description 'Set the thickness for the button border.' +complete -c swaynag -l button-gap -fr --description 'Set the size of the gap between buttons.' +complete -c swaynag -l button-dismiss-gap -fr --description 'Set the size of the gap between the dismiss button and another button.' +complete -c swaynag -l button-margin-right -fr --description 'Set the margin from the right of the dismiss button to edge.' +complete -c swaynag -l button-padding -fr --description 'Set the padding for the button text.' diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 0ba45d4a9..3f42a77d7 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -25,13 +25,15 @@ types=( 'get_binding_modes' 'get_config' 'send_tick' +'subscribe' ) _arguments -s \ - '(-v --version)'{-v,--version}'[Show the version number and quit]' \ - '(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \ '(-h --help)'{-h,--help}'[Show help message and quit]' \ + '(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \ + '(-p --pretty)'{-p,--pretty}'[Use pretty output even when not using a tty]' \ '(-q --quiet)'{-q,--quiet}'[Be quiet]' \ '(-r --raw)'{-r,--raw}'[Use raw output even if using a tty]' \ '(-s --socket)'{-s,--socket}'[Use the specified socket path]:files:_files' \ - '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}' + '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}' \ + '(-v --version)'{-v,--version}'[Show the version number and quit]' diff --git a/config.in b/config.in index fbd952353..1ba0443ab 100644 --- a/config.in +++ b/config.in @@ -17,7 +17,7 @@ set $right l set $term urxvt # Your preferred application launcher # Note: it's recommended that you pass the final command to sway -set $menu dmenu_path | dmenu | xargs swaymsg exec +set $menu dmenu_path | dmenu | xargs swaymsg exec -- ### Output configuration # @@ -62,13 +62,13 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # Basics: # - # start a terminal + # Start a terminal bindsym $mod+Return exec $term - # kill focused window + # Kill focused window bindsym $mod+Shift+q kill - # start your launcher + # Start your launcher bindsym $mod+d exec $menu # Drag floating windows by holding down $mod and left mouse button. @@ -78,10 +78,10 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # mouse button for dragging. floating_modifier $mod normal - # reload the configuration file + # Reload the configuration file bindsym $mod+Shift+c reload - # exit sway (logs you out of your Wayland session) + # Exit sway (logs you out of your Wayland session) bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' # # Moving around: @@ -91,18 +91,18 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill bindsym $mod+$down focus down bindsym $mod+$up focus up bindsym $mod+$right focus right - # or use $mod+[up|down|left|right] + # Or use $mod+[up|down|left|right] bindsym $mod+Left focus left bindsym $mod+Down focus down bindsym $mod+Up focus up bindsym $mod+Right focus right - # _move_ the focused window with the same, but add Shift + # Move the focused window with the same, but add Shift bindsym $mod+Shift+$left move left bindsym $mod+Shift+$down move down bindsym $mod+Shift+$up move up bindsym $mod+Shift+$right move right - # ditto, with arrow keys + # Ditto, with arrow keys bindsym $mod+Shift+Left move left bindsym $mod+Shift+Down move down bindsym $mod+Shift+Up move up @@ -110,7 +110,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # Workspaces: # - # switch to workspace + # Switch to workspace bindsym $mod+1 workspace 1 bindsym $mod+2 workspace 2 bindsym $mod+3 workspace 3 @@ -121,7 +121,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill bindsym $mod+8 workspace 8 bindsym $mod+9 workspace 9 bindsym $mod+0 workspace 10 - # move focused container to workspace + # Move focused container to workspace bindsym $mod+Shift+1 move container to workspace 1 bindsym $mod+Shift+2 move container to workspace 2 bindsym $mod+Shift+3 move container to workspace 3 @@ -157,7 +157,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # Swap focus between the tiling area and the floating area bindsym $mod+space focus mode_toggle - # move focus to the parent container + # Move focus to the parent container bindsym $mod+a focus parent # # Scratchpad: @@ -184,13 +184,13 @@ mode "resize" { bindsym $up resize shrink height 10px bindsym $right resize grow width 10px - # ditto, with arrow keys + # Ditto, with arrow keys bindsym Left resize shrink width 10px bindsym Down resize grow height 10px bindsym Up resize shrink height 10px bindsym Right resize grow width 10px - # return to default mode + # Return to default mode bindsym Return mode "default" bindsym Escape mode "default" } diff --git a/include/cairo.h b/include/cairo.h index d1b9b8d78..c1275db20 100644 --- a/include/cairo.h +++ b/include/cairo.h @@ -1,13 +1,9 @@ #ifndef _SWAY_CAIRO_H #define _SWAY_CAIRO_H - #include "config.h" #include #include #include -#if HAVE_GDK_PIXBUF -#include -#endif void cairo_set_source_u32(cairo_t *cairo, uint32_t color); cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel); @@ -15,11 +11,4 @@ cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); -#if HAVE_GDK_PIXBUF - -cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( - const GdkPixbuf *gdkbuf); - -#endif // HAVE_GDK_PIXBUF - #endif diff --git a/include/ipc-client.h b/include/ipc-client.h index c9f5b3441..d3895023f 100644 --- a/include/ipc-client.h +++ b/include/ipc-client.h @@ -1,7 +1,9 @@ #ifndef _SWAY_IPC_CLIENT_H #define _SWAY_IPC_CLIENT_H +#include #include +#include #include "ipc.h" @@ -36,5 +38,9 @@ struct ipc_response *ipc_recv_response(int socketfd); * Free ipc_response struct */ void free_ipc_response(struct ipc_response *response); +/** + * Sets the receive timeout for the IPC socket + */ +bool ipc_set_recv_timeout(int socketfd, struct timeval tv); #endif diff --git a/include/stringop.h b/include/stringop.h index f7ca60a51..6f920999a 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -4,7 +4,6 @@ #include "list.h" void strip_whitespace(char *str); -char *strip_comments(char *str); void strip_quotes(char *str); // strcat that does nothing if dest or src is NULL @@ -21,22 +20,10 @@ list_t *split_string(const char *str, const char *delims); char **split_args(const char *str, int *argc); void free_argv(int argc, char **argv); -char *code_strchr(const char *string, char delimiter); -char *code_strstr(const char *haystack, const char *needle); int unescape_string(char *string); char *join_args(char **argv, int argc); -char *join_list(list_t *list, char *separator); -/** - * Add quotes around any argv with whitespaces. - */ -void add_quotes(char **argv, int argc); - -// split string into 2 by delim. -char *cmdsep(char **stringp, const char *delim); // Split string into 2 by delim, handle quotes char *argsep(char **stringp, const char *delim); -const char *strcasestr(const char *haystack, const char *needle); - #endif diff --git a/include/sway/commands.h b/include/sway/commands.h index 764821a0e..c4903788c 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -101,6 +101,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; +sway_cmd cmd_bindswitch; sway_cmd cmd_bindsym; sway_cmd cmd_border; sway_cmd cmd_client_noop; @@ -135,6 +136,7 @@ sway_cmd cmd_fullscreen; sway_cmd cmd_gaps; sway_cmd cmd_hide_edge_borders; sway_cmd cmd_include; +sway_cmd cmd_inhibit_idle; sway_cmd cmd_input; sway_cmd cmd_seat; sway_cmd cmd_ipc; @@ -145,6 +147,8 @@ sway_cmd cmd_mark; sway_cmd cmd_mode; sway_cmd cmd_mouse_warping; sway_cmd cmd_move; +sway_cmd cmd_new_float; +sway_cmd cmd_new_window; sway_cmd cmd_nop; sway_cmd cmd_opacity; sway_cmd cmd_new_float; @@ -177,6 +181,9 @@ sway_cmd cmd_title_align; sway_cmd cmd_title_format; sway_cmd cmd_titlebar_border_thickness; sway_cmd cmd_titlebar_padding; +sway_cmd cmd_unbindcode; +sway_cmd cmd_unbindswitch; +sway_cmd cmd_unbindsym; sway_cmd cmd_unmark; sway_cmd cmd_urgent; sway_cmd cmd_workspace; @@ -210,6 +217,8 @@ sway_cmd bar_cmd_tray_bindcode; sway_cmd bar_cmd_tray_bindsym; sway_cmd bar_cmd_tray_output; sway_cmd bar_cmd_tray_padding; +sway_cmd bar_cmd_unbindcode; +sway_cmd bar_cmd_unbindsym; sway_cmd bar_cmd_wrap_scroll; sway_cmd bar_cmd_workspace_buttons; @@ -260,6 +269,8 @@ sway_cmd output_cmd_enable; sway_cmd output_cmd_mode; sway_cmd output_cmd_position; sway_cmd output_cmd_scale; +sway_cmd output_cmd_subpixel; +sway_cmd output_cmd_toggle; sway_cmd output_cmd_transform; sway_cmd seat_cmd_attach; diff --git a/include/sway/config.h b/include/sway/config.h index 54cdcc908..b94a35f3f 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "../include/config.h" @@ -23,12 +24,12 @@ struct sway_variable { char *value; }; - enum binding_input_type { BINDING_KEYCODE, BINDING_KEYSYM, BINDING_MOUSECODE, BINDING_MOUSESYM, + BINDING_SWITCH }; enum binding_flags { @@ -37,6 +38,7 @@ enum binding_flags { BINDING_BORDER=4, // mouse only; trigger on container border BINDING_CONTENTS=8, // mouse only; trigger on container contents BINDING_TITLEBAR=16, // mouse only; trigger on container titlebar + BINDING_CODE=32, // keyboard only; convert keysyms into keycodes }; /** @@ -48,6 +50,7 @@ struct sway_binding { char *input; uint32_t flags; list_t *keys; // sorted in ascending order + list_t *syms; // sorted in ascending order; NULL if BINDING_CODE is not set uint32_t modifiers; char *command; }; @@ -60,6 +63,16 @@ struct sway_mouse_binding { char *command; }; +/** + * A laptop switch binding and an associated command. + */ +struct sway_switch_binding { + enum wlr_switch_type type; + enum wlr_switch_state state; + uint32_t flags; + char *command; +}; + /** * Focus on window activation. */ @@ -78,6 +91,7 @@ struct sway_mode { list_t *keysym_bindings; list_t *keycode_bindings; list_t *mouse_bindings; + list_t *switch_bindings; bool pango; }; @@ -92,6 +106,7 @@ struct input_config_mapped_from_region { */ struct input_config { char *identifier; + const char *input_type; int accel_profile; int click_method; @@ -171,6 +186,7 @@ struct output_config { int x, y; float scale; int32_t transform; + enum wl_output_subpixel subpixel; char *background; char *background_option; @@ -200,6 +216,10 @@ struct workspace_config { }; struct bar_config { + char *swaybar_command; + struct wl_client *client; + struct wl_listener client_destroy; + /** * One of "dock", "hide", "invisible" * @@ -227,7 +247,6 @@ struct bar_config { list_t *bindings; char *status_command; bool pango_markup; - char *swaybar_command; char *font; int height; // -1 not defined bool workspace_buttons; @@ -238,7 +257,6 @@ struct bar_config { bool binding_mode_indicator; bool verbose; struct side_gaps gaps; - pid_t pid; int status_padding; int status_edge_padding; struct { @@ -404,13 +422,13 @@ struct sway_config { list_t *workspace_configs; list_t *output_configs; list_t *input_configs; + list_t *input_type_configs; list_t *seat_configs; list_t *criteria; list_t *no_focus; list_t *active_bar_modifiers; struct sway_mode *current_mode; struct bar_config *current_bar; - char *swaybg_command; uint32_t floating_mod; bool floating_mod_inverse; uint32_t dragging_key; @@ -433,6 +451,11 @@ struct sway_config { enum sway_popup_during_fullscreen popup_during_fullscreen; bool xwayland; + // swaybg + char *swaybg_command; + struct wl_client *swaybg_client; + struct wl_listener swaybg_client_destroy; + // Flags enum focus_follows_mouse_mode focus_follows_mouse; enum mouse_warping_mode mouse_warping; @@ -465,6 +488,7 @@ struct sway_config { int floating_border_thickness; enum edge_border_types hide_edge_borders; enum edge_border_types saved_edge_borders; + bool hide_lone_tab; // border colors struct { @@ -487,6 +511,9 @@ struct sway_config { list_t *feature_policies; list_t *ipc_policies; + // The keysym to keycode translation + struct xkb_state *keysym_translation_state; + // Context for command handlers struct { struct input_config *input_config; @@ -513,7 +540,7 @@ bool load_main_config(const char *path, bool is_active, bool validating); /** * Loads an included config. Can only be used after load_main_config. */ -bool load_include_configs(const char *path, struct sway_config *config, +void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag); /** @@ -552,6 +579,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src); struct input_config *store_input_config(struct input_config *ic); +void input_config_fill_rule_names(struct input_config *ic, + struct xkb_rule_names *rules); + void free_input_config(struct input_config *ic); int seat_name_cmp(const void *item, const void *data); @@ -584,30 +614,28 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output); struct output_config *store_output_config(struct output_config *oc); +struct output_config *find_output_config(struct sway_output *output); + void apply_output_config_to_outputs(struct output_config *oc); void reset_outputs(void); void free_output_config(struct output_config *oc); +bool spawn_swaybg(void); + int workspace_output_cmp_workspace(const void *a, const void *b); -int sway_binding_cmp(const void *a, const void *b); - -int sway_binding_cmp_qsort(const void *a, const void *b); - -int sway_binding_cmp_keys(const void *a, const void *b); - void free_sway_binding(struct sway_binding *sb); +void free_switch_binding(struct sway_switch_binding *binding); + void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); void load_swaybar(struct bar_config *bar); void load_swaybars(void); -void terminate_swaybg(pid_t pid); - struct bar_config *default_bar_config(void); void free_bar_config(struct bar_config *bar); @@ -626,6 +654,16 @@ void free_workspace_config(struct workspace_config *wsc); */ void config_update_font_height(bool recalculate); +/** + * Convert bindsym into bindcode using the first configured layout. + * Return false in case the conversion is unsuccessful. + */ +bool translate_binding(struct sway_binding *binding); + +void translate_keysyms(struct input_config *input_config); + +void binding_add_translated(struct sway_binding *binding, list_t *bindings); + /* Global config singleton. */ extern struct sway_config *config; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 3eb583d5a..8a1d9e5ef 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -20,6 +20,7 @@ struct criteria { char *cmdlist; char *target; // workspace or output name for `assign` criteria + bool autofail; // __focused__ while no focus or n/a for focused view pcre *title; pcre *shell; pcre *app_id; @@ -35,7 +36,7 @@ struct criteria { bool floating; bool tiling; char urgent; // 'l' for latest or 'o' for oldest - char *workspace; + pcre *workspace; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/include/sway/debug.h b/include/sway/debug.h deleted file mode 100644 index 0e9bb0565..000000000 --- a/include/sway/debug.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef SWAY_DEBUG_H -#define SWAY_DEBUG_H -#include - -struct sway_debug { - bool noatomic; // Ignore atomic layout updates - bool render_tree; // Render the tree overlay - bool txn_timings; // Log verbose messages about transactions - bool txn_wait; // Always wait for the timeout before applying - - enum { - DAMAGE_DEFAULT, // Default behaviour - DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged - DAMAGE_RERENDER, // Render the full output when any damage occurs - } damage; -}; - -extern struct sway_debug debug; - -void update_debug_tree(void); - -#endif diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index e5ed8a3de..4d4e59b0b 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -4,6 +4,14 @@ #include #include "sway/server.h" +enum sway_idle_inhibit_mode { + INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) + INHIBIT_IDLE_FOCUS, // User set inhibitor when focused + INHIBIT_IDLE_FULLSCREEN, // User set inhibitor when fullscreen + visible + INHIBIT_IDLE_OPEN, // User set inhibitor while open + INHIBIT_IDLE_VISIBLE // User set inhibitor when visible +}; + struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; @@ -15,14 +23,24 @@ struct sway_idle_inhibit_manager_v1 { struct sway_idle_inhibitor_v1 { struct sway_idle_inhibit_manager_v1 *manager; struct sway_view *view; + enum sway_idle_inhibit_mode mode; struct wl_list link; struct wl_listener destroy; }; -void idle_inhibit_v1_check_active( +void sway_idle_inhibit_v1_check_active( struct sway_idle_inhibit_manager_v1 *manager); +void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, + enum sway_idle_inhibit_mode mode); + +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( + struct sway_view *view); + +void sway_idle_inhibit_v1_user_inhibitor_destroy( + struct sway_idle_inhibitor_v1 *inhibitor); + struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( struct wl_display *wl_display, struct wlr_idle *idle); #endif diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 98eb4679b..516718c97 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -52,8 +52,6 @@ struct sway_cursor { struct wl_event_source *hide_source; bool hidden; - // Mouse binding state - uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; size_t pressed_button_count; }; @@ -78,13 +76,6 @@ void cursor_handle_activity(struct sway_cursor *cursor); void cursor_unhide(struct sway_cursor *cursor); int cursor_get_timeout(struct sway_cursor *cursor); -/** - * Like cursor_rebase, but also allows focus to change when the cursor enters a - * new container. - */ -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, double sx, double sy); - void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, enum wlr_button_state state); diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index e166a2377..8d4a5b005 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -62,4 +62,6 @@ struct input_config *input_device_get_config(struct sway_input_device *device); char *input_device_get_identifier(struct wlr_input_device *device); +const char *input_device_get_type(struct sway_input_device *device); + #endif diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 0c8ada0f7..b86220535 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -65,6 +65,8 @@ struct sway_keyboard { struct sway_binding *repeat_binding; }; +struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic); + struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, struct sway_seat_device *device); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 1c9354df4..eb6c09a1d 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -9,18 +9,25 @@ struct sway_seat; struct sway_seatop_impl { - void (*motion)(struct sway_seat *seat, uint32_t time_msec); - void (*finish)(struct sway_seat *seat); - void (*abort)(struct sway_seat *seat); + void (*button)(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state); + void (*motion)(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy); + void (*axis)(struct sway_seat *seat, struct wlr_event_pointer_axis *event); + void (*rebase)(struct sway_seat *seat, uint32_t time_msec); + void (*end)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); + bool allow_set_cursor; }; struct sway_seat_device { struct sway_seat *sway_seat; struct sway_input_device *input_device; struct sway_keyboard *keyboard; + struct sway_switch *switch_device; struct wl_list link; // sway_seat::devices }; @@ -68,14 +75,13 @@ struct sway_seat { // Seat operations (drag and resize) const struct sway_seatop_impl *seatop_impl; void *seatop_data; - uint32_t seatop_button; - uint32_t last_button; uint32_t last_button_serial; struct wl_listener focus_destroy; struct wl_listener new_node; - struct wl_listener new_drag_icon; + struct wl_listener request_start_drag; + struct wl_listener start_drag; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; @@ -137,6 +143,11 @@ struct sway_node *seat_get_focus(struct sway_seat *seat); struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat); +// If a scratchpad container is fullscreen global, this can be used to try to +// determine the last focused workspace. Otherwise, this should yield the same +// results as seat_get_focused_workspace. +struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat); + struct sway_container *seat_get_focused_container(struct sway_seat *seat); /** @@ -184,23 +195,25 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); -void seatop_begin_down(struct sway_seat *seat, - struct sway_container *con, uint32_t button, int sx, int sy); +void seatop_begin_default(struct sway_seat *seat); + +void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, + uint32_t time_msec, int sx, int sy); void seatop_begin_move_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_resize_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge); + struct sway_container *con, enum wlr_edges edge); void seatop_begin_resize_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge); + struct sway_container *con, enum wlr_edges edge); struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, struct sway_workspace *workspace); @@ -210,19 +223,24 @@ void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, void seat_consider_warp_to_focus(struct sway_seat *seat); -bool seat_doing_seatop(struct sway_seat *seat); - -void seatop_motion(struct sway_seat *seat, uint32_t time_msec); +void seatop_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state); /** - * End a seatop and apply the affects. + * dx and dy are distances relative to previous position. */ -void seatop_finish(struct sway_seat *seat); +void seatop_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy); + +void seatop_axis(struct sway_seat *seat, struct wlr_event_pointer_axis *event); + +void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); /** - * End a seatop without applying the affects. + * End a seatop (ie. free any seatop specific resources). */ -void seatop_abort(struct sway_seat *seat); +void seatop_end(struct sway_seat *seat); /** * Instructs the seatop implementation to drop any references to the given @@ -238,5 +256,6 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); +bool seatop_allows_set_cursor(struct sway_seat *seat); #endif diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h new file mode 100644 index 000000000..19bb1e772 --- /dev/null +++ b/include/sway/input/switch.h @@ -0,0 +1,19 @@ +#ifndef _SWAY_INPUT_SWITCH_H +#define _SWAY_INPUT_SWITCH_H + +#include "sway/input/seat.h" + +struct sway_switch { + struct sway_seat_device *seat_device; + + struct wl_listener switch_toggle; +}; + +struct sway_switch *sway_switch_create(struct sway_seat *seat, + struct sway_seat_device *device); + +void sway_switch_configure(struct sway_switch *sway_switch); + +void sway_switch_destroy(struct sway_switch *sway_switch); + +#endif diff --git a/include/sway/output.h b/include/sway/output.h index ea7a21741..3d430ea26 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -29,16 +29,15 @@ struct sway_output { struct timespec last_frame; struct wlr_output_damage *damage; - int lx, ly; - int width, height; + int lx, ly; // layout coords + int width, height; // transformed buffer size + enum wl_output_subpixel detected_subpixel; bool enabled, configured; list_t *workspaces; struct sway_output_state current; - struct wl_client *swaybg_client; - struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; @@ -46,7 +45,6 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; - struct wl_listener swaybg_client_destroy; struct { struct wl_signal destroy; @@ -84,11 +82,13 @@ void output_damage_box(struct sway_output *output, struct wlr_box *box); void output_damage_whole_container(struct sway_output *output, struct sway_container *con); +// this ONLY includes the enabled outputs struct sway_output *output_by_name_or_id(const char *name_or_id); -void output_sort_workspaces(struct sway_output *output); +// this includes all the outputs, including disabled ones +struct sway_output *all_output_by_name_or_id(const char *name_or_id); -struct output_config *output_find_config(struct sway_output *output); +void output_sort_workspaces(struct sway_output *output); void output_enable(struct sway_output *output, struct output_config *oc); @@ -144,7 +144,7 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void render_rect(struct wlr_output *wlr_output, +void render_rect(struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]); @@ -154,4 +154,8 @@ void scale_box(struct wlr_box *box, float scale); enum wlr_direction opposite_direction(enum wlr_direction d); +void handle_output_manager_apply(struct wl_listener *listener, void *data); + +void handle_output_manager_test(struct wl_listener *listener, void *data); + #endif diff --git a/include/sway/server.h b/include/sway/server.h index 5eef7c1a9..fd613bb66 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,10 @@ struct sway_server { struct wlr_pointer_constraints_v1 *pointer_constraints; struct wl_listener pointer_constraint; + struct wlr_output_manager_v1 *output_manager_v1; + struct wl_listener output_manager_apply; + struct wl_listener output_manager_test; + size_t txn_timeout_ms; list_t *transactions; list_t *dirty_nodes; @@ -74,6 +79,20 @@ struct sway_server { struct sway_server server; +struct sway_debug { + bool noatomic; // Ignore atomic layout updates + bool txn_timings; // Log verbose messages about transactions + bool txn_wait; // Always wait for the timeout before applying + + enum { + DAMAGE_DEFAULT, // Default behaviour + DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged + DAMAGE_RERENDER, // Render the full output when any damage occurs + } damage; +}; + +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); diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 5a1787392..74d9ea18c 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h @@ -1,9 +1,12 @@ #ifndef _SWAY_SWAYNAG_H #define _SWAY_SWAYNAG_H +#include struct swaynag_instance { + struct wl_client *client; + struct wl_listener client_destroy; + const char *args; - pid_t pid; int fd[2]; bool detailed; }; @@ -15,9 +18,6 @@ struct swaynag_instance { bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag); -// Kill the swaynag instance -void swaynag_kill(struct swaynag_instance *swaynag); - // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed // is false. void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index f7a4ac37e..8448d7059 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -212,10 +212,12 @@ void container_update_representation(struct sway_container *container); */ size_t container_titlebar_height(void); -/** - * Resize and center the container in its workspace. - */ -void container_init_floating(struct sway_container *container); +void floating_calculate_constraints(int *min_width, int *max_width, + int *min_height, int *max_height); + +void container_floating_resize_and_center(struct sway_container *con); + +void container_floating_set_default_size(struct sway_container *con); void container_set_floating(struct sway_container *container, bool enable); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 9ff45eb5f..c4f84207d 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -21,8 +21,6 @@ struct sway_root { #endif struct wl_list drag_icons; // sway_drag_icon::link - struct wlr_texture *debug_tree; - // Includes disabled outputs struct wl_list all_outputs; // sway_output::link @@ -87,4 +85,6 @@ 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 diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 5cc9777b4..4ce487fca 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -192,8 +192,11 @@ struct sway_view_child_impl { */ struct sway_view_child { const struct sway_view_child_impl *impl; + struct wl_list link; struct sway_view *view; + struct sway_view_child *parent; + struct wl_list children; // sway_view_child::link struct wlr_surface *surface; bool mapped; @@ -202,6 +205,7 @@ struct sway_view_child { struct wl_listener surface_map; struct wl_listener surface_unmap; struct wl_listener surface_destroy; + struct wl_listener view_unmap; }; struct sway_subsurface { @@ -313,7 +317,7 @@ void view_destroy(struct sway_view *view); void view_begin_destroy(struct sway_view *view); void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, - bool fullscreen, bool decoration); + bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); void view_unmap(struct sway_view *view); diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 2518d5aa8..10984ab0c 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -23,6 +23,7 @@ struct swaybar { // only relevant when bar is in "hide" mode bool visible_by_modifier; bool visible_by_urgency; + bool visible_by_mode; bool visible; struct wl_display *display; @@ -30,10 +31,8 @@ struct swaybar { struct zwlr_layer_shell_v1 *layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; struct wl_shm *shm; - struct wl_seat *seat; struct swaybar_config *config; - struct swaybar_pointer pointer; struct status_line *status; struct loop *eventloop; @@ -42,6 +41,7 @@ struct swaybar { int ipc_socketfd; struct wl_list outputs; // swaybar_output::link + struct wl_list seats; // swaybar_seat::link #if HAVE_TRAY struct swaybar_tray *tray; @@ -57,6 +57,7 @@ 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 diff --git a/include/swaybar/input.h b/include/swaybar/input.h index d76cd551f..2d38f7a71 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -22,6 +22,19 @@ struct swaybar_pointer { uint32_t serial; }; +struct touch_slot { + int32_t id; + uint32_t time; + struct swaybar_output *output; + double start_x, start_y; + double x, y; +}; + +struct swaybar_touch { + struct wl_touch *touch; + struct touch_slot slots[16]; +}; + enum hotspot_event_handling { HOTSPOT_IGNORE, HOTSPOT_PROCESS, @@ -37,12 +50,23 @@ struct swaybar_hotspot { void *data; }; +struct swaybar_seat { + struct swaybar *bar; + uint32_t wl_name; + struct wl_seat *wl_seat; + struct swaybar_pointer pointer; + struct swaybar_touch touch; + struct wl_list link; // swaybar_seat:link +}; + extern const struct wl_seat_listener seat_listener; -void update_cursor(struct swaybar *bar); +void update_cursor(struct swaybar_seat *seat); uint32_t event_to_x11_button(uint32_t event); void free_hotspots(struct wl_list *list); +void swaybar_seat_free(struct swaybar_seat *seat); + #endif diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 2183ce224..7f8f62212 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -8,22 +8,26 @@ struct swaynag_type { char *output; uint32_t anchors; + // Colors uint32_t button_background; uint32_t background; uint32_t text; uint32_t border; uint32_t border_bottom; - uint32_t bar_border_thickness; - uint32_t message_padding; - uint32_t details_border_thickness; - uint32_t button_border_thickness; - uint32_t button_gap; - uint32_t button_gap_close; - uint32_t button_margin_right; - uint32_t button_padding; + // Sizing + ssize_t bar_border_thickness; + ssize_t message_padding; + ssize_t details_border_thickness; + ssize_t button_border_thickness; + ssize_t button_gap; + ssize_t button_gap_close; + ssize_t button_margin_right; + ssize_t button_padding; }; +struct swaynag_type *swaynag_type_new(const char *name); + void swaynag_types_add_default(list_t *types); struct swaynag_type *swaynag_type_get(list_t *types, char *name); diff --git a/include/util.h b/include/util.h index 1fd772c0c..6d9454e05 100644 --- a/include/util.h +++ b/include/util.h @@ -3,6 +3,7 @@ #include #include +#include /** * Wrap i into the range [0, max[ @@ -29,4 +30,8 @@ bool parse_boolean(const char *boolean, bool current); */ float parse_float(const char *value); +const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); + +bool set_cloexec(int fd, bool cloexec); + #endif diff --git a/meson.build b/meson.build index d3172bdd8..f657f874e 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '1.0', + version: '1.1', license: 'MIT', meson_version: '>=0.48.0', default_options: [ @@ -57,20 +57,22 @@ elogind = dependency('libelogind', version: '>=239', required: false) xcb = dependency('xcb', required: get_option('xwayland')) math = cc.find_library('m') rt = cc.find_library('rt') -git = find_program('git', required: false) +git = find_program('git', native: true, required: false) # Try first to find wlroots as a subproject, then as a system dependency +wlroots_version = '>=0.6' wlroots_proj = subproject( 'wlroots', default_options: ['rootston=false', 'examples=false'], required: false, + version: wlroots_version, ) if wlroots_proj.found() wlroots = wlroots_proj.get_variable('wlroots') wlroots_conf = wlroots_proj.get_variable('conf_data') wlroots_has_xwayland = wlroots_conf.get('WLR_HAS_XWAYLAND') == 1 else - wlroots = dependency('wlroots') + wlroots = dependency('wlroots', version: wlroots_version) wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include ', dependencies: wlroots) == '1' endif @@ -93,21 +95,19 @@ conf_data.set10('HAVE_SYSTEMD', systemd.found()) conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -if not systemd.found() and not elogind.found() - warning('The sway binary must be setuid when compiled without (e)logind') - warning('You must do this manually post-install: chmod a+s /path/to/sway') -endif - -scdoc = find_program('scdoc', required: get_option('man-pages')) +scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() - sh = find_program('sh') + scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) + sh = find_program('sh', native: true) mandir = get_option('mandir') man_files = [ 'sway/sway.1.scd', 'sway/sway.5.scd', 'sway/sway-bar.5.scd', 'sway/sway-input.5.scd', + 'sway/sway-ipc.7.scd', 'sway/sway-output.5.scd', + 'swaybar/swaybar-protocol.7.scd', 'swaymsg/swaymsg.1.scd', 'swaynag/swaynag.1.scd', 'swaynag/swaynag.5.scd', @@ -122,7 +122,7 @@ if scdoc.found() input: filename, output: output, command: [ - sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.path(), output) + sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) ], install: true, install_dir: '@0@/man@1@'.format(mandir, section) @@ -151,7 +151,6 @@ subdir('sway') subdir('swaymsg') subdir('client') -subdir('swaybg') subdir('swaybar') subdir('swaynag') @@ -235,3 +234,21 @@ if get_option('fish-completions') install_data(fish_files, install_dir: fish_install_dir) endif + +status = [ + '', + 'Features:', + 'xwayland: @0@'.format(have_xwayland), + 'gdk-pixbuf: @0@'.format(gdk_pixbuf.found()), + 'systemd: @0@'.format(systemd.found()), + 'elogind: @0@'.format(elogind.found()), + 'tray: @0@'.format(have_tray), + 'man-pages: @0@'.format(scdoc.found()), + '', +] +message('\n'.join(status)) + +if not systemd.found() and not elogind.found() + warning('The sway binary must be setuid when compiled without (e)logind') + warning('You must do this manually post-install: chmod a+s /path/to/sway') +endif diff --git a/sway.desktop b/sway.desktop index 98c9af293..420db5aa1 100644 --- a/sway.desktop +++ b/sway.desktop @@ -1,5 +1,5 @@ [Desktop Entry] Name=Sway -Comment=SirCmpwn's Wayland window manager +Comment=An i3-compatible Wayland compositor Exec=sway Type=Application diff --git a/sway/commands.c b/sway/commands.c index 3fc4f86e4..237bfc281 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -47,6 +47,7 @@ static struct cmd_handler handlers[] = { { "assign", cmd_assign }, { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, + { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "client.background", cmd_client_noop }, { "client.focused", cmd_client_focused }, @@ -76,8 +77,8 @@ static struct cmd_handler handlers[] = { { "input", cmd_input }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, - { "new_float", cmd_default_floating_border }, - { "new_window", cmd_default_border }, + { "new_float", cmd_new_float }, + { "new_window", cmd_new_window }, { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "popup_during_fullscreen", cmd_popup_during_fullscreen }, @@ -91,6 +92,9 @@ static struct cmd_handler handlers[] = { { "title_align", cmd_title_align }, { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, + { "unbindcode", cmd_unbindcode }, + { "unbindswitch", cmd_unbindswitch }, + { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, }; @@ -111,6 +115,7 @@ static struct cmd_handler command_handlers[] = { { "exit", cmd_exit }, { "floating", cmd_floating }, { "fullscreen", cmd_fullscreen }, + { "inhibit_idle", cmd_inhibit_idle }, { "kill", cmd_kill }, { "layout", cmd_layout }, { "mark", cmd_mark }, @@ -255,7 +260,8 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, int argc; char **argv = split_args(cmd, &argc); if (strcmp(argv[0], "exec") != 0 && - strcmp(argv[0], "exec_always") != 0) { + strcmp(argv[0], "exec_always") != 0 && + strcmp(argv[0], "mode") != 0) { int i; for (i = 1; i < argc; ++i) { if (*argv[i] == '\"' || *argv[i] == '\'') { @@ -274,7 +280,6 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, // Var replacement, for all but first argument of set for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { argv[i] = do_var_replacement(argv[i]); - unescape_string(argv[i]); } if (!config->handler_context.using_criteria) { @@ -385,7 +390,9 @@ struct cmd_results *config_command(char *exec, char **new_block) { && handler->handle != cmd_mode && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode + && handler->handle != cmd_bindswitch && handler->handle != cmd_set + && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { strip_quotes(argv[i]); } diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 82441f9e4..88580ffb0 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -32,6 +32,8 @@ static struct cmd_handler bar_handlers[] = { { "tray_bindsym", bar_cmd_tray_bindsym }, { "tray_output", bar_cmd_tray_output }, { "tray_padding", bar_cmd_tray_padding }, + { "unbindcode", bar_cmd_unbindcode }, + { "unbindsym", bar_cmd_unbindsym }, { "workspace_buttons", bar_cmd_workspace_buttons }, { "wrap_scroll", bar_cmd_wrap_scroll }, }; @@ -78,28 +80,38 @@ struct cmd_results *cmd_bar(int argc, char **argv) { } config->current_bar = bar; ++argv; --argc; + } else if (!config->reading && strcmp(argv[0], "mode") != 0 && + strcmp(argv[0], "hidden_state") != 0) { + if (is_subcommand(argv[0])) { + return cmd_results_new(CMD_INVALID, "No bar defined."); + } else { + return cmd_results_new(CMD_INVALID, + "Unknown/invalid command '%s'", argv[1]); + } } - if (!config->current_bar && config->reading) { - // Create new bar with default values - struct bar_config *bar = default_bar_config(); - if (!bar) { - return cmd_results_new(CMD_FAILURE, - "Unable to allocate bar state"); - } + if (!config->current_bar) { + if (config->reading) { + // Create new bar with default values + struct bar_config *bar = default_bar_config(); + if (!bar) { + return cmd_results_new(CMD_FAILURE, + "Unable to allocate bar state"); + } - // set bar id - const int len = snprintf(NULL, 0, "bar-%d", config->bars->length - 1) + 1; - bar->id = malloc(len * sizeof(char)); - if (bar->id) { - snprintf(bar->id, len, "bar-%d", config->bars->length - 1); - } else { - return cmd_results_new(CMD_FAILURE, "Unable to allocate bar ID"); - } + // set bar id + int len = snprintf(NULL, 0, "bar-%d", config->bars->length - 1) + 1; + bar->id = malloc(len * sizeof(char)); + if (bar->id) { + snprintf(bar->id, len, "bar-%d", config->bars->length - 1); + } else { + return cmd_results_new(CMD_FAILURE, "Unable to allocate bar ID"); + } - // Set current bar - config->current_bar = bar; - sway_log(SWAY_DEBUG, "Creating bar %s", bar->id); + // Set current bar + config->current_bar = bar; + sway_log(SWAY_DEBUG, "Creating bar %s", bar->id); + } } if (find_handler(argv[0], bar_config_handlers, diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index 17030a05f..b4b5bc459 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c @@ -9,14 +9,71 @@ #include "log.h" #include "stringop.h" -static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) { - const char *command = code ? "bar bindcode" : "bar bindsym"; - struct cmd_results *error = NULL; - if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, 2))) { - return error; +static struct cmd_results *binding_add(struct bar_binding *binding, + list_t *mode_bindings) { + const char *name = get_mouse_button_name(binding->button); + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; i++) { + struct bar_binding *other = mode_bindings->items[i]; + if (other->button == binding->button && + other->release == binding->release) { + overwritten = true; + mode_bindings->items[i] = binding; + free_bar_binding(other); + sway_log(SWAY_DEBUG, "[bar %s] Updated binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + break; + } } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "[bar %s] Added binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + } + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *binding_remove(struct bar_binding *binding, + list_t *mode_bindings) { + const char *name = get_mouse_button_name(binding->button); + for (int i = 0; i < mode_bindings->length; i++) { + struct bar_binding *other = mode_bindings->items[i]; + if (other->button == binding->button && + other->release == binding->release) { + sway_log(SWAY_DEBUG, "[bar %s] Unbound binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + free_bar_binding(other); + free_bar_binding(binding); + list_del(mode_bindings, i); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + + struct cmd_results *error = cmd_results_new(CMD_FAILURE, "Could not " + "find binding for [bar %s]" " Button %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + free_bar_binding(binding); + return error; +} + +static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, + bool unbind) { + int minargs = 2; + const char *command; + if (unbind) { + minargs--; + command = code ? "bar unbindcode" : "bar unbindsym"; + } else { + command = code ? "bar bindcode" : "bar bindsym"; + } + + struct cmd_results *error = NULL; + if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, minargs))) { + return error; } struct bar_binding *binding = calloc(1, sizeof(struct bar_binding)); @@ -46,39 +103,27 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) { free_bar_binding(binding); return cmd_results_new(CMD_INVALID, "Unknown button %s", argv[0]); } - const char *name = get_mouse_button_name(binding->button); + list_t *bindings = config->current_bar->bindings; + if (unbind) { + return binding_remove(binding, bindings); + } binding->command = join_args(argv + 1, argc - 1); - - list_t *bindings = config->current_bar->bindings; - bool overwritten = false; - for (int i = 0; i < bindings->length; i++) { - struct bar_binding *other = bindings->items[i]; - if (other->button == binding->button && - other->release == binding->release) { - overwritten = true; - bindings->items[i] = binding; - free_bar_binding(other); - sway_log(SWAY_DEBUG, "[bar %s] Updated binding for %u (%s)%s", - config->current_bar->id, binding->button, name, - binding->release ? " - release" : ""); - break; - } - } - if (!overwritten) { - list_add(bindings, binding); - sway_log(SWAY_DEBUG, "[bar %s] Added binding for %u (%s)%s", - config->current_bar->id, binding->button, name, - binding->release ? " - release" : ""); - } - - return cmd_results_new(CMD_SUCCESS, NULL); + return binding_add(binding, bindings); } struct cmd_results *bar_cmd_bindcode(int argc, char **argv) { - return bar_cmd_bind(argc, argv, true); + return bar_cmd_bind(argc, argv, true, false); } struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { - return bar_cmd_bind(argc, argv, false); + return bar_cmd_bind(argc, argv, false, false); +} + +struct cmd_results *bar_cmd_unbindcode(int argc, char **argv) { + return bar_cmd_bind(argc, argv, true, true); +} + +struct cmd_results *bar_cmd_unbindsym(int argc, char **argv) { + return bar_cmd_bind(argc, argv, false, true); } diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index 29c93ddc6..b58d8a838 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c @@ -10,10 +10,7 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - config->current_bar->binding_mode_indicator = + config->current_bar->binding_mode_indicator = parse_boolean(argv[0], config->current_bar->binding_mode_indicator); if (config->current_bar->binding_mode_indicator) { sway_log(SWAY_DEBUG, "Enabling binding mode indicator on bar: %s", diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index cf1f759ec..62987f3e3 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -9,9 +9,6 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } char *font = join_args(argv, argc); free(config->current_bar->font); config->current_bar->font = font; diff --git a/sway/commands/bar/gaps.c b/sway/commands/bar/gaps.c index 83480fb50..f99966b40 100644 --- a/sway/commands/bar/gaps.c +++ b/sway/commands/bar/gaps.c @@ -13,9 +13,6 @@ struct cmd_results *bar_cmd_gaps(int argc, char **argv) { if ((error = checkarg(argc, "gaps", EXPECTED_AT_MOST, 4))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } int top = 0, right = 0, bottom = 0, left = 0; diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index 54b7b16e3..6ac07843d 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c @@ -12,10 +12,6 @@ struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - sway_log(SWAY_DEBUG, "[Bar %s] Setting icon theme to %s", config->current_bar->id, argv[0]); free(config->current_bar->icon_theme); diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 68a80abf6..1081ad4b8 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -20,6 +20,8 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode bar->mode = strdup("hide"); } else if (strcasecmp("invisible", mode) == 0) { bar->mode = strdup("invisible"); + } else if (strcasecmp("overlay", mode) == 0) { + bar->mode = strdup("overlay"); } else { return cmd_results_new(CMD_INVALID, "Invalid value %s", mode); } diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index d25d01d4b..983d21795 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c @@ -10,10 +10,6 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - uint32_t mod = 0; if (strcmp(argv[0], "none") != 0) { list_t *split = split_string(argv[0], "+"); diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index 956c19597..6a78b30d0 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c @@ -10,9 +10,6 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } const char *output = argv[0]; list_t *outputs = config->current_bar->outputs; diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index b0958cf11..ee51390da 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c @@ -9,11 +9,8 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - config->current_bar->pango_markup - = parse_boolean(argv[0], config->current_bar->pango_markup); + config->current_bar->pango_markup = + parse_boolean(argv[0], config->current_bar->pango_markup); if (config->current_bar->pango_markup) { sway_log(SWAY_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id); diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index 4456d7241..b207de0bf 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c @@ -9,9 +9,6 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) { if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } char *valid[] = { "top", "bottom" }; for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { if (strcasecmp(valid[i], argv[0]) == 0) { diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 76e99b490..6737d4d24 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c @@ -8,9 +8,6 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) { if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } free(config->current_bar->separator_symbol); config->current_bar->separator_symbol = strdup(argv[0]); sway_log(SWAY_DEBUG, "Settings separator_symbol '%s' for bar: %s", diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c index 0b58e5fa2..77a73ab60 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c @@ -8,9 +8,6 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) { if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } free(config->current_bar->status_command); config->current_bar->status_command = NULL; diff --git a/sway/commands/bar/strip_workspace_name.c b/sway/commands/bar/strip_workspace_name.c index 1aa393599..764321a89 100644 --- a/sway/commands/bar/strip_workspace_name.c +++ b/sway/commands/bar/strip_workspace_name.c @@ -10,9 +10,6 @@ struct cmd_results *bar_cmd_strip_workspace_name(int argc, char **argv) { "strip_workspace_name", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } config->current_bar->strip_workspace_name = parse_boolean(argv[0], config->current_bar->strip_workspace_name); diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c index 56c4c4a12..2d7fe1a7f 100644 --- a/sway/commands/bar/strip_workspace_numbers.c +++ b/sway/commands/bar/strip_workspace_numbers.c @@ -10,10 +10,7 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - + config->current_bar->strip_workspace_numbers = parse_boolean(argv[0], config->current_bar->strip_workspace_numbers); diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c index b54bafa9f..0892a898a 100644 --- a/sway/commands/bar/swaybar_command.c +++ b/sway/commands/bar/swaybar_command.c @@ -8,9 +8,6 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) { if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } free(config->current_bar->swaybar_command); config->current_bar->swaybar_command = join_args(argv, argc); sway_log(SWAY_DEBUG, "Using custom swaybar command: %s", diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c index 7fe67c423..c910d1065 100644 --- a/sway/commands/bar/tray_bind.c +++ b/sway/commands/bar/tray_bind.c @@ -12,9 +12,6 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { if ((error = checkarg(argc, command, EXPECTED_EQUAL_TO, 2))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } struct tray_binding *binding = calloc(1, sizeof(struct tray_binding)); if (!binding) { diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index 16e16f603..8bfdf1939 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c @@ -13,10 +13,6 @@ struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - list_t *outputs = config->current_bar->tray_outputs; if (!outputs) { config->current_bar->tray_outputs = outputs = create_list(); diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c index f43cfe4fe..f90b6003f 100644 --- a/sway/commands/bar/tray_padding.c +++ b/sway/commands/bar/tray_padding.c @@ -15,9 +15,6 @@ struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } struct bar_config *bar = config->current_bar; char *end; diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c index 792ef605a..6bfb16165 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c @@ -9,10 +9,7 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - config->current_bar->workspace_buttons = + config->current_bar->workspace_buttons = parse_boolean(argv[0], config->current_bar->workspace_buttons); if (config->current_bar->workspace_buttons) { sway_log(SWAY_DEBUG, "Enabling workspace buttons on bar: %s", diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index decd238d4..f57e393df 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c @@ -9,10 +9,7 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - config->current_bar->wrap_scroll = + config->current_bar->wrap_scroll = parse_boolean(argv[0], config->current_bar->wrap_scroll); if (config->current_bar->wrap_scroll) { sway_log(SWAY_DEBUG, "Enabling wrap scroll on bar: %s", diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 172e6b8ae..d43c87fb0 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -24,11 +24,38 @@ void free_sway_binding(struct sway_binding *binding) { } list_free_items_and_destroy(binding->keys); + list_free_items_and_destroy(binding->syms); free(binding->input); free(binding->command); free(binding); } +void free_switch_binding(struct sway_switch_binding *binding) { + if (!binding) { + return; + } + free(binding->command); + free(binding); +} + +/** + * Returns true if the bindings have the same switch type and state combinations. + */ +static bool binding_switch_compare(struct sway_switch_binding *binding_a, + struct sway_switch_binding *binding_b) { + if (binding_a->type != binding_b->type) { + return false; + } + if (binding_a->state != binding_b->state) { + return false; + } + if ((binding_a->flags & BINDING_LOCKED) != + (binding_b->flags & BINDING_LOCKED)) { + return false; + } + return true; +} + /** * Returns true if the bindings have the same key and modifier combinations. * Note that keyboard layout is not considered, so the bindings might actually @@ -45,7 +72,7 @@ static bool binding_key_compare(struct sway_binding *binding_a, } uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER - | BINDING_CONTENTS | BINDING_TITLEBAR; + | BINDING_CONTENTS | BINDING_TITLEBAR | BINDING_LOCKED; if ((binding_a->flags & conflict_generating_flags) != (binding_b->flags & conflict_generating_flags)) { return false; @@ -174,12 +201,133 @@ static struct cmd_results *identify_key(const char* name, bool first_key, return NULL; } +static struct cmd_results *switch_binding_add( + struct sway_switch_binding *binding, const char *bindtype, + const char *switchcombo, bool warn) { + list_t *mode_bindings = config->current_mode->switch_bindings; + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", + switchcombo, binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' to `%s` from `%s`", + switchcombo, binding->command, + config_binding->command); + } + free_switch_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s`", + bindtype, switchcombo, binding->command); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *switch_binding_remove( + struct sway_switch_binding *binding, const char *bindtype, + const char *switchcombo) { + list_t *mode_bindings = config->current_mode->switch_bindings; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + free_switch_binding(config_binding); + free_switch_binding(binding); + list_del(mode_bindings, i); + sway_log(SWAY_DEBUG, "%s - Unbound %s switch", + bindtype, switchcombo); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, "Could not find switch binding `%s`", + switchcombo); +} + +/** + * Insert or update the binding. + * Return the binding which has been replaced or NULL. + */ +static struct sway_binding *binding_upsert(struct sway_binding *binding, + list_t *mode_bindings) { + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_binding *config_binding = mode_bindings->items[i]; + if (binding_key_compare(binding, config_binding)) { + mode_bindings->items[i] = binding; + return config_binding; + } + } + + list_add(mode_bindings, binding); + return NULL; +} + +static struct cmd_results *binding_add(struct sway_binding *binding, + list_t *mode_bindings, const char *bindtype, + const char *keycombo, bool warn) { + struct sway_binding *config_binding = binding_upsert(binding, mode_bindings); + + if (config_binding) { + sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " + "to `%s` from `%s`", keycombo, binding->input, + binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' for device '%s' to `%s` from `%s`", + keycombo, binding->input, binding->command, + config_binding->command); + } + free_sway_binding(config_binding); + } else { + sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'", + bindtype, keycombo, binding->command, binding->input); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *binding_remove(struct sway_binding *binding, + list_t *mode_bindings, const char *bindtype, + const char *keycombo) { + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_binding *config_binding = mode_bindings->items[i]; + if (binding_key_compare(binding, config_binding)) { + sway_log(SWAY_DEBUG, "%s - Unbound `%s` from device '%s'", + bindtype, keycombo, binding->input); + free_sway_binding(config_binding); + free_sway_binding(binding); + list_del(mode_bindings, i); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + free_sway_binding(binding); + return cmd_results_new(CMD_FAILURE, "Could not find binding `%s` " + "for the given flags", keycombo); +} + static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, - bool bindcode) { - const char *bindtype = bindcode ? "bindcode" : "bindsym"; + bool bindcode, bool unbind) { + const char *bindtype; + int minargs = 2; + if (unbind) { + bindtype = bindcode ? "unbindcode" : "unbindsym"; + minargs--; + } else { + bindtype = bindcode ? "bindcode": "bindsym"; + } struct cmd_results *error = NULL; - if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { return error; } @@ -196,7 +344,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, bool exclude_titlebar = false; bool warn = true; - // Handle --release and --locked while (argc > 0) { if (strcmp("--release", argv[0]) == 0) { binding->flags |= BINDING_RELEASE; @@ -206,6 +353,10 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR; } else if (strcmp("--border", argv[0]) == 0) { binding->flags |= BINDING_BORDER; + } else if (strcmp("--to-code", argv[0]) == 0) { + if (!bindcode) { + binding->flags |= BINDING_CODE; + } } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { exclude_titlebar = true; } else if (strncmp("--input-device=", argv[0], @@ -226,15 +377,14 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, BINDING_MOUSECODE : BINDING_MOUSESYM; } - if (argc < 2) { + if (argc < minargs) { free_sway_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command " - "(expected at least 2 non-option arguments, got %d)", bindtype, argc); + "(expected at least %d non-option arguments, got %d)", + bindtype, minargs, argc); } - binding->command = join_args(argv + 1, argc - 1); - list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for a modifier key @@ -265,7 +415,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_add(binding->keys, key); } list_free_items_and_destroy(split); - binding->order = binding_order++; // refine region of interest for mouse binding once we are certain // that this is one @@ -279,6 +428,12 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, // sort ascending list_qsort(binding->keys, key_qsort_cmp); + // translate keysyms into keycodes + if (!translate_binding(binding)) { + sway_log(SWAY_INFO, + "Unable to translate bindsym into bindcode: %s", argv[0]); + } + list_t *mode_bindings; if (binding->type == BINDING_KEYCODE) { mode_bindings = config->current_mode->keycode_bindings; @@ -288,41 +443,116 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, mode_bindings = config->current_mode->mouse_bindings; } - // overwrite the binding if it already exists - bool overwritten = false; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_binding *config_binding = mode_bindings->items[i]; - if (binding_key_compare(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " - "from `%s` to `%s`", argv[0], binding->input, - binding->command, config_binding->command); - if (warn) { - config_add_swaynag_warning("Overwriting binding" - "'%s' for device '%s' to `%s` from `%s`", - argv[0], binding->input, binding->command, - config_binding->command); - } - free_sway_binding(config_binding); - mode_bindings->items[i] = binding; - overwritten = true; + if (unbind) { + return binding_remove(binding, mode_bindings, bindtype, argv[0]); + } + + binding->command = join_args(argv + 1, argc - 1); + binding->order = binding_order++; + return binding_add(binding, mode_bindings, bindtype, argv[0], warn); +} + +struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, + bool unbind) { + int minargs = 2; + char *bindtype = "bindswitch"; + if (unbind) { + minargs--; + bindtype = "unbindswitch"; + } + + struct cmd_results *error = NULL; + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { + return error; + } + struct sway_switch_binding *binding = calloc(1, sizeof(struct sway_switch_binding)); + if (!binding) { + return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); + } + + bool warn = true; + + // Handle flags + while (argc > 0) { + if (strcmp("--locked", argv[0]) == 0) { + binding->flags |= BINDING_LOCKED; + } else if (strcmp("--no-warn", argv[0]) == 0) { + warn = false; + } else { + break; } + argv++; + argc--; } - if (!overwritten) { - list_add(mode_bindings, binding); + 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); } - sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'", - bindtype, argv[0], binding->command, binding->input); - return cmd_results_new(CMD_SUCCESS, NULL); + list_t *split = split_string(argv[0], ":"); + if (split->length != 2) { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid %s command (expected binding with the form " + ":)", bindtype, argc); + } + if (strcmp(split->items[0], "tablet") == 0) { + binding->type = WLR_SWITCH_TYPE_TABLET_MODE; + } else if (strcmp(split->items[0], "lid") == 0) { + binding->type = WLR_SWITCH_TYPE_LID; + } else { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid %s command (expected switch binding: " + "unknown switch %s)", bindtype, split->items[0]); + } + if (strcmp(split->items[1], "on") == 0) { + binding->state = WLR_SWITCH_STATE_ON; + } else if (strcmp(split->items[1], "off") == 0) { + binding->state = WLR_SWITCH_STATE_OFF; + } else if (strcmp(split->items[1], "toggle") == 0) { + binding->state = WLR_SWITCH_STATE_TOGGLE; + } else { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid %s command " + "(expected switch state: unknown state %d)", + bindtype, split->items[0]); + } + list_free_items_and_destroy(split); + + if (unbind) { + return switch_binding_remove(binding, bindtype, argv[0]); + } + binding->command = join_args(argv + 1, argc - 1); + return switch_binding_add(binding, bindtype, argv[0], warn); } struct cmd_results *cmd_bindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false); + return cmd_bindsym_or_bindcode(argc, argv, false, false); } struct cmd_results *cmd_bindcode(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, true); + return cmd_bindsym_or_bindcode(argc, argv, true, false); +} + +struct cmd_results *cmd_unbindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false, true); +} + +struct cmd_results *cmd_unbindcode(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, true, true); +} + +struct cmd_results *cmd_bindswitch(int argc, char **argv) { + return cmd_bind_or_unbind_switch(argc, argv, false); +} + +struct cmd_results *cmd_unbindswitch(int argc, char **argv) { + return cmd_bind_or_unbind_switch(argc, argv, true); } /** @@ -360,3 +590,115 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) ipc_event_binding(binding); } } + +/** + * The last found keycode associated with the keysym + * and the total count of matches. + */ +struct keycode_matches { + xkb_keysym_t keysym; + xkb_keycode_t keycode; + int count; +}; + +/** + * Iterate through keycodes in the keymap to find ones matching + * the specified keysym. + */ +static void find_keycode(struct xkb_keymap *keymap, + xkb_keycode_t keycode, void *data) { + xkb_keysym_t keysym = xkb_state_key_get_one_sym( + config->keysym_translation_state, keycode); + + if (keysym == XKB_KEY_NoSymbol) { + return; + } + + struct keycode_matches *matches = data; + if (matches->keysym == keysym) { + matches->keycode = keycode; + matches->count++; + } +} + +/** + * Return the keycode for the specified keysym. + */ +static struct keycode_matches get_keycode_for_keysym(xkb_keysym_t keysym) { + struct keycode_matches matches = { + .keysym = keysym, + .keycode = XKB_KEYCODE_INVALID, + .count = 0, + }; + + xkb_keymap_key_for_each( + xkb_state_get_keymap(config->keysym_translation_state), + find_keycode, &matches); + return matches; +} + +bool translate_binding(struct sway_binding *binding) { + if ((binding->flags & BINDING_CODE) == 0) { + return true; + } + + switch (binding->type) { + // a bindsym to translate + case BINDING_KEYSYM: + binding->syms = binding->keys; + binding->keys = create_list(); + break; + // a bindsym to re-translate + case BINDING_KEYCODE: + list_free_items_and_destroy(binding->keys); + binding->keys = create_list(); + break; + default: + return true; + } + + for (int i = 0; i < binding->syms->length; ++i) { + xkb_keysym_t *keysym = binding->syms->items[i]; + struct keycode_matches matches = get_keycode_for_keysym(*keysym); + + if (matches.count != 1) { + sway_log(SWAY_INFO, "Unable to convert keysym %d into" + " a single keycode (found %d matches)", + *keysym, matches.count); + goto error; + } + + xkb_keycode_t *keycode = malloc(sizeof(xkb_keycode_t)); + if (!keycode) { + sway_log(SWAY_ERROR, "Unable to allocate memory for a keycode"); + goto error; + } + + *keycode = matches.keycode; + list_add(binding->keys, keycode); + } + + list_qsort(binding->keys, key_qsort_cmp); + binding->type = BINDING_KEYCODE; + return true; + +error: + list_free_items_and_destroy(binding->keys); + binding->type = BINDING_KEYSYM; + binding->keys = binding->syms; + binding->syms = NULL; + return false; +} + +void binding_add_translated(struct sway_binding *binding, + list_t *mode_bindings) { + struct sway_binding *config_binding = + binding_upsert(binding, mode_bindings); + + if (config_binding) { + sway_log(SWAY_INFO, "Overwriting binding for device '%s' " + "to `%s` from `%s`", binding->input, + binding->command, config_binding->command); + free_sway_binding(config_binding); + } +} diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 821093695..ce1233454 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -32,6 +32,11 @@ struct cmd_results *cmd_floating(int argc, char **argv) { seat_set_focus_container(config->handler_context.seat, container); } + if (container_is_scratchpad_hidden(container)) { + return cmd_results_new(CMD_INVALID, + "Can't change floating on hidden scratchpad container"); + } + // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(container)) { @@ -45,7 +50,10 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); - arrange_workspace(container->workspace); + // Floating containers in the scratchpad should be ignored + if (container->workspace) { + arrange_workspace(container->workspace); + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 25df51304..8baa616dd 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -1,3 +1,4 @@ +#include #include #include #include "log.h" @@ -90,8 +91,9 @@ static struct sway_node *get_node_in_output_direction( return &ws->node; } -static struct sway_node *node_get_in_direction(struct sway_container *container, - struct sway_seat *seat, enum wlr_direction dir) { +static struct sway_node *node_get_in_direction_tiling( + struct sway_container *container, struct sway_seat *seat, + enum wlr_direction dir) { struct sway_container *wrap_candidate = NULL; struct sway_container *current = container; while (current) { @@ -172,6 +174,41 @@ static struct sway_node *node_get_in_direction(struct sway_container *container, return NULL; } +static struct sway_node *node_get_in_direction_floating( + struct sway_container *con, struct sway_seat *seat, + enum wlr_direction dir) { + double ref_lx = con->x + con->width / 2; + double ref_ly = con->y + con->height / 2; + double closest_distance = DBL_MAX; + struct sway_container *closest_con = NULL; + + if (!con->workspace) { + return NULL; + } + + for (int i = 0; i < con->workspace->floating->length; i++) { + struct sway_container *floater = con->workspace->floating->items[i]; + if (floater == con) { + continue; + } + float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT + ? (floater->x + floater->width / 2) - ref_lx + : (floater->y + floater->height / 2) - ref_ly; + if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { + distance = -distance; + } + if (distance < 0) { + continue; + } + if (distance < closest_distance) { + closest_distance = distance; + closest_con = floater; + } + } + + return closest_con ? &closest_con->node : NULL; +} + static struct cmd_results *focus_mode(struct sway_workspace *ws, struct sway_seat *seat, bool floating) { struct sway_container *new_focus = NULL; @@ -208,6 +245,11 @@ static struct cmd_results *focus_output(struct sway_seat *seat, "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"); + } output = output_get_in_direction(ws->output, direction); if (!output) { @@ -279,6 +321,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) { } seat_set_focus_container(seat, container); seat_consider_warp_to_focus(seat); + container_raise_floating(container); return cmd_results_new(CMD_SUCCESS, NULL); } @@ -325,11 +368,19 @@ struct cmd_results *cmd_focus(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL); } - struct sway_node *next_focus = - node_get_in_direction(container, seat, direction); + struct sway_node *next_focus = NULL; + if (container_is_floating(container)) { + next_focus = node_get_in_direction_floating(container, seat, direction); + } else { + next_focus = node_get_in_direction_tiling(container, seat, direction); + } if (next_focus) { seat_set_focus(seat, next_focus); seat_consider_warp_to_focus(seat); + + if (next_focus->type == N_CONTAINER) { + container_raise_floating(next_focus->sway_container); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c index e646ae9b7..fafc1c3ef 100644 --- a/sway/commands/force_focus_wrapping.c +++ b/sway/commands/force_focus_wrapping.c @@ -1,9 +1,16 @@ -#include #include "sway/commands.h" #include "sway/config.h" +#include "log.h" #include "util.h" struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: force_focus_wrapping is deprecated. " + "Use focus_wrapping instead."); + if (config->reading) { + config_add_swaynag_warning("force_focus_wrapping is deprecated. " + "Use focus_wrapping instead."); + } + struct cmd_results *error = checkarg(argc, "force_focus_wrapping", EXPECTED_EQUAL_TO, 1); if (error) { diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 52248ce4a..a268ba034 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -26,6 +26,13 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { "Can't fullscreen an empty workspace"); } + // If in the scratchpad, operate on the highest container + if (container && !container->workspace) { + while (container->parent) { + container = container->parent; + } + } + bool is_fullscreen = container && container->fullscreen_mode != FULLSCREEN_NONE; bool global = false; diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 84a217b89..f69bece10 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -4,11 +4,25 @@ #include "sway/tree/view.h" struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { + const char *expected_syntax = "Expected 'hide_edge_borders [--i3] " + "none|vertical|horizontal|both|smart|smart_no_gaps"; + struct cmd_results *error = NULL; - if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_AT_LEAST, 1))) { return error; } + bool hide_lone_tab = false; + if (strcmp(*argv, "--i3") == 0) { + hide_lone_tab = true; + ++argv; + --argc; + } + + if (!argc) { + return cmd_results_new(CMD_INVALID, expected_syntax); + } + if (strcmp(argv[0], "none") == 0) { config->hide_edge_borders = E_NONE; } else if (strcmp(argv[0], "vertical") == 0) { @@ -22,9 +36,9 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { } else if (strcmp(argv[0], "smart_no_gaps") == 0) { config->hide_edge_borders = E_SMART_NO_GAPS; } else { - return cmd_results_new(CMD_INVALID, "Expected 'hide_edge_borders " - "'"); + return cmd_results_new(CMD_INVALID, expected_syntax); } + config->hide_lone_tab = hide_lone_tab; config->saved_edge_borders = config->hide_edge_borders; arrange_root(); diff --git a/sway/commands/include.c b/sway/commands/include.c index d18098564..d4c14c35f 100644 --- a/sway/commands/include.c +++ b/sway/commands/include.c @@ -7,11 +7,8 @@ struct cmd_results *cmd_include(int argc, char **argv) { return error; } - if (!load_include_configs(argv[0], config, - &config->swaynag_config_errors)) { - return cmd_results_new(CMD_INVALID, - "Failed to include sub configuration file: %s", argv[0]); - } + // We don't care if the included config(s) fails to load. + load_include_configs(argv[0], config, &config->swaynag_config_errors); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c new file mode 100644 index 000000000..aebc2bf9f --- /dev/null +++ b/sway/commands/inhibit_idle.c @@ -0,0 +1,51 @@ +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/desktop/idle_inhibit_v1.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" + +struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "inhibit_idle", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + struct sway_container *con = config->handler_context.container; + if (!con || !con->view) { + return cmd_results_new(CMD_INVALID, + "Only views can have idle inhibitors"); + } + + bool clear = false; + enum sway_idle_inhibit_mode mode; + if (strcmp(argv[0], "focus") == 0) { + mode = INHIBIT_IDLE_FOCUS; + } else if (strcmp(argv[0], "fullscreen") == 0) { + mode = INHIBIT_IDLE_FULLSCREEN; + } else if (strcmp(argv[0], "open") == 0) { + mode = INHIBIT_IDLE_OPEN; + } else if (strcmp(argv[0], "none") == 0) { + clear = true; + } else if (strcmp(argv[0], "visible") == 0) { + mode = INHIBIT_IDLE_VISIBLE; + } else { + return cmd_results_new(CMD_INVALID, + "Expected `inhibit_idle focus|fullscreen|open|none|visible`"); + } + + struct sway_idle_inhibitor_v1 *inhibitor = + sway_idle_inhibit_v1_user_inhibitor_for_view(con->view); + if (inhibitor) { + if (clear) { + sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); + } else { + inhibitor->mode = mode; + sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + } + } else if (!clear) { + sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/input.c b/sway/commands/input.c index b72bd76ba..0195082c3 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -39,6 +39,23 @@ static struct cmd_handler input_config_handlers[] = { { "xkb_numlock", input_cmd_xkb_numlock }, }; +/** + * Re-translate keysyms if a change in the input config could affect them. + */ +static void retranslate_keysyms(struct input_config *input_config) { + for (int i = 0; i < config->input_configs->length; ++i) { + struct input_config *ic = config->input_configs->items[i]; + if (ic->xkb_layout) { + // this is the first config with xkb_layout + if (ic->identifier == input_config->identifier) { + translate_keysyms(ic); + } + + return; + } + } +} + struct cmd_results *cmd_input(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) { @@ -73,6 +90,7 @@ struct cmd_results *cmd_input(int argc, char **argv) { store_input_config(config->handler_context.input_config); input_manager_apply_input_config(ic); + retranslate_keysyms(ic); } else { free_input_config(config->handler_context.input_config); } diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 7d61c3be3..32f8fb520 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -147,7 +147,11 @@ struct cmd_results *cmd_layout(int argc, char **argv) { workspace->layout = new_layout; workspace_update_representation(workspace); } - arrange_workspace(workspace); + if (root->fullscreen_global) { + arrange_root(); + } else { + arrange_workspace(workspace); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/mode.c b/sway/commands/mode.c index d424eb1ce..ef2c5d794 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -12,7 +12,9 @@ // Must be in order for the bsearch static struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, - { "bindsym", cmd_bindsym } + { "bindswitch", cmd_bindswitch }, + { "bindsym", cmd_bindsym }, + { "set", cmd_set }, }; struct cmd_results *cmd_mode(int argc, char **argv) { @@ -54,6 +56,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->keysym_bindings = create_list(); mode->keycode_bindings = create_list(); mode->mouse_bindings = create_list(); + mode->switch_bindings = create_list(); mode->pango = pango; list_add(config->modes, mode); } diff --git a/sway/commands/move.c b/sway/commands/move.c index 4306aac86..9c6e69ece 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -28,6 +28,15 @@ static const char expected_syntax[] = static struct sway_output *output_in_direction(const char *direction_string, struct sway_output *reference, int ref_lx, int ref_ly) { + if (strcasecmp(direction_string, "current") == 0) { + struct sway_workspace *active_ws = + seat_get_focused_workspace(config->handler_context.seat); + if (!active_ws) { + return NULL; + } + return active_ws->output; + } + struct { char *name; enum wlr_direction direction; @@ -311,37 +320,39 @@ static bool container_move_in_direction(struct sway_container *container, while (current) { list_t *siblings = container_get_siblings(current); - enum sway_container_layout layout = container_parent_layout(current); - int index = list_find(siblings, current); - int desired = index + offs; + if (siblings) { + enum sway_container_layout layout = container_parent_layout(current); + int index = list_find(siblings, current); + int desired = index + offs; - // Don't allow containers to move out of their - // fullscreen or floating parent - if (current->fullscreen_mode || container_is_floating(current)) { - return false; - } + // Don't allow containers to move out of their + // fullscreen or floating parent + if (current->fullscreen_mode || container_is_floating(current)) { + return false; + } - if (is_parallel(layout, move_dir)) { - if (desired == -1 || desired == siblings->length) { - if (current->parent == container->parent) { - current = current->parent; - continue; - } else { - // Reparenting - if (current->parent) { - container_insert_child(current->parent, container, - index + (offs < 0 ? 0 : 1)); + if (is_parallel(layout, move_dir)) { + if (desired == -1 || desired == siblings->length) { + if (current->parent == container->parent) { + current = current->parent; + continue; } else { - workspace_insert_tiling(current->workspace, container, - index + (offs < 0 ? 0 : 1)); + // Reparenting + if (current->parent) { + container_insert_child(current->parent, container, + index + (offs < 0 ? 0 : 1)); + } else { + workspace_insert_tiling(current->workspace, container, + index + (offs < 0 ? 0 : 1)); + } + return true; } + } else { + // Container can move within its siblings + container_move_to_container_from_direction(container, + siblings->items[desired], move_dir); return true; } - } else { - // Container can move within its siblings - container_move_to_container_from_direction(container, - siblings->items[desired], move_dir); - return true; } } @@ -350,33 +361,36 @@ static bool container_move_in_direction(struct sway_container *container, // Maybe rejigger the workspace struct sway_workspace *ws = container->workspace; - if (!is_parallel(ws->layout, move_dir)) { - workspace_rejigger(ws, container, move_dir); - return true; - } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { - workspace_rejigger(ws, container, move_dir); - return true; - } - - // Try adjacent output - struct sway_output *output = - output_get_in_direction(container->workspace->output, move_dir); - if (output) { - struct sway_workspace *ws = output_get_active_workspace(output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return false; + if (ws) { + if (!is_parallel(ws->layout, move_dir)) { + workspace_rejigger(ws, container, move_dir); + return true; + } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { + workspace_rejigger(ws, container, move_dir); + return true; } - container_move_to_workspace_from_direction(container, ws, move_dir); - return true; + + // Try adjacent output + struct sway_output *output = + output_get_in_direction(container->workspace->output, move_dir); + if (output) { + struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return false; + } + container_move_to_workspace_from_direction(container, ws, move_dir); + return true; + } + sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); } - sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); return false; } -static struct cmd_results *cmd_move_container(int argc, char **argv) { +static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, + int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move container/window", - EXPECTED_AT_LEAST, 3))) { + EXPECTED_AT_LEAST, 2))) { return error; } @@ -391,27 +405,9 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { container = workspace_wrap_children(workspace); } - bool no_auto_back_and_forth = false; - while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { - no_auto_back_and_forth = true; - if (--argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); - } - ++argv; - } - while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) { - no_auto_back_and_forth = true; - if (--argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); - } - argv++; - } - - while (strcasecmp(argv[1], "to") == 0) { - if (--argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); - } - argv++; + if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return cmd_results_new(CMD_FAILURE, + "Can't move fullscreen global container"); } struct sway_seat *seat = config->handler_context.seat; @@ -421,18 +417,18 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { struct sway_node *destination = NULL; // determine destination - if (strcasecmp(argv[1], "workspace") == 0) { + if (strcasecmp(argv[0], "workspace") == 0) { // move container to workspace x struct sway_workspace *ws = NULL; char *ws_name = NULL; - if (strcasecmp(argv[2], "next") == 0 || - strcasecmp(argv[2], "prev") == 0 || - strcasecmp(argv[2], "next_on_output") == 0 || - strcasecmp(argv[2], "prev_on_output") == 0 || - strcasecmp(argv[2], "current") == 0) { - ws = workspace_by_name(argv[2]); - } else if (strcasecmp(argv[2], "back_and_forth") == 0) { - if (!(ws = workspace_by_name(argv[2]))) { + if (strcasecmp(argv[1], "next") == 0 || + strcasecmp(argv[1], "prev") == 0 || + strcasecmp(argv[1], "next_on_output") == 0 || + strcasecmp(argv[1], "prev_on_output") == 0 || + strcasecmp(argv[1], "current") == 0) { + ws = workspace_by_name(argv[1]); + } else if (strcasecmp(argv[1], "back_and_forth") == 0) { + if (!(ws = workspace_by_name(argv[1]))) { if (seat->prev_workspace_name) { ws_name = strdup(seat->prev_workspace_name); } else { @@ -441,19 +437,19 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { } } } else { - if (strcasecmp(argv[2], "number") == 0) { - // move "container to workspace number x" - if (argc < 4) { + if (strcasecmp(argv[1], "number") == 0) { + // move [window|container] [to] "workspace number x" + if (argc < 3) { return cmd_results_new(CMD_INVALID, expected_syntax); } - if (!isdigit(argv[3][0])) { + if (!isdigit(argv[2][0])) { return cmd_results_new(CMD_INVALID, - "Invalid workspace number '%s'", argv[3]); + "Invalid workspace number '%s'", argv[2]); } - ws_name = join_args(argv + 3, argc - 3); + ws_name = join_args(argv + 2, argc - 2); ws = workspace_by_number(ws_name); } else { - ws_name = join_args(argv + 2, argc - 2); + ws_name = join_args(argv + 1, argc - 1); ws = workspace_by_name(ws_name); } @@ -473,7 +469,7 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // We have to create the workspace, but if the container is // sticky and the workspace is going to be created on the same // output, we'll bail out first. - if (container->is_sticky) { + if (container->is_sticky && container_is_floating_or_child(container)) { struct sway_output *new_output = workspace_get_initial_output(ws_name); if (old_output == new_output) { @@ -488,27 +484,27 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { free(ws_name); struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); destination = dst ? &dst->node : &ws->node; - } else if (strcasecmp(argv[1], "output") == 0) { - struct sway_output *new_output = output_in_direction(argv[2], + } else if (strcasecmp(argv[0], "output") == 0) { + struct sway_output *new_output = output_in_direction(argv[1], old_output, container->x, container->y); if (!new_output) { return cmd_results_new(CMD_FAILURE, - "Can't find output with name/direction '%s'", argv[2]); + "Can't find output with name/direction '%s'", argv[1]); } destination = seat_get_focus_inactive(seat, &new_output->node); - } else if (strcasecmp(argv[1], "mark") == 0) { - struct sway_container *dest_con = container_find_mark(argv[2]); + } else if (strcasecmp(argv[0], "mark") == 0) { + struct sway_container *dest_con = container_find_mark(argv[1]); if (dest_con == NULL) { return cmd_results_new(CMD_FAILURE, - "Mark '%s' not found", argv[2]); + "Mark '%s' not found", argv[1]); } destination = &dest_con->node; } else { return cmd_results_new(CMD_INVALID, expected_syntax); } - if (container->is_sticky && old_output && - node_has_ancestor(destination, &old_output->node)) { + if (container->is_sticky && container_is_floating_or_child(container) && + old_output && node_has_ancestor(destination, &old_output->node)) { return cmd_results_new(CMD_FAILURE, "Can't move sticky " "container to another workspace on the same output"); } @@ -626,30 +622,32 @@ static void workspace_move_to_output(struct sway_workspace *workspace, static struct cmd_results *cmd_move_workspace(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 1))) { return error; } - while (strcasecmp(argv[1], "to") == 0) { - if (--argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); - } - ++argv; + if (strcasecmp(argv[0], "output") == 0) { + --argc; ++argv; } - if (strcasecmp(argv[1], "output") != 0) { - return cmd_results_new(CMD_INVALID, expected_syntax); + if (!argc) { + return cmd_results_new(CMD_INVALID, + "Expected 'move workspace to [output] '"); } struct sway_workspace *workspace = config->handler_context.workspace; + if (!workspace) { + return cmd_results_new(CMD_FAILURE, "No workspace to move"); + } + struct sway_output *old_output = workspace->output; int center_x = workspace->width / 2 + workspace->x, center_y = workspace->height / 2 + workspace->y; - struct sway_output *new_output = output_in_direction(argv[2], + struct sway_output *new_output = output_in_direction(argv[0], old_output, center_x, center_y); if (!new_output) { return cmd_results_new(CMD_FAILURE, - "Can't find output with name/direction '%s'", argv[2]); + "Can't find output with name/direction '%s'", argv[0]); } workspace_move_to_output(workspace, new_output); @@ -662,9 +660,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { static struct cmd_results *cmd_move_in_direction( enum wlr_direction direction, int argc, char **argv) { int move_amt = 10; - if (argc > 1) { + if (argc) { char *inv; - move_amt = (int)strtol(argv[1], &inv, 10); + move_amt = (int)strtol(argv[0], &inv, 10); if (*inv != '\0' && strcasecmp(inv, "px") != 0) { return cmd_results_new(CMD_FAILURE, "Invalid distance specified"); } @@ -756,7 +754,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { } if (!argc) { - return cmd_results_new(CMD_FAILURE, expected_position_syntax); + return cmd_results_new(CMD_INVALID, expected_position_syntax); } bool absolute = false; @@ -766,17 +764,20 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { ++argv; } if (!argc) { - return cmd_results_new(CMD_FAILURE, expected_position_syntax); + return cmd_results_new(CMD_INVALID, expected_position_syntax); } if (strcmp(argv[0], "position") == 0) { --argc; ++argv; } if (!argc) { - return cmd_results_new(CMD_FAILURE, expected_position_syntax); + return cmd_results_new(CMD_INVALID, expected_position_syntax); } if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || strcmp(argv[0], "pointer") == 0) { + if (absolute) { + return cmd_results_new(CMD_INVALID, expected_position_syntax); + } struct sway_seat *seat = config->handler_context.seat; if (!seat->cursor) { return cmd_results_new(CMD_FAILURE, "No cursor device"); @@ -862,6 +863,18 @@ static struct cmd_results *cmd_move_to_scratchpad(void) { return cmd_results_new(CMD_SUCCESS, NULL); } +static const char expected_full_syntax[] = "Expected " + "'move left|right|up|down [ [px]]'" + " or 'move [--no-auto-back-and-forth] [window|container] [to] workspace" + " |next|prev|next_on_output|prev_on_output|current|(number )'" + " or 'move [window|container] [to] output |left|right|up|down'" + " or 'move [window|container] [to] mark '" + " or 'move [window|container] [to] scratchpad'" + " or 'move workspace to [output] |left|right|up|down'" + " or 'move [window|container] [to] [absolute] position [px] [px]'" + " or 'move [window|container] [to] [absolute] position center'" + " or 'move [window|container] [to] position mouse|cursor|pointer'"; + struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { @@ -873,31 +886,55 @@ struct cmd_results *cmd_move(int argc, char **argv) { } if (strcasecmp(argv[0], "left") == 0) { - return cmd_move_in_direction(WLR_DIRECTION_LEFT, argc, argv); + return cmd_move_in_direction(WLR_DIRECTION_LEFT, --argc, ++argv); } else if (strcasecmp(argv[0], "right") == 0) { - return cmd_move_in_direction(WLR_DIRECTION_RIGHT, argc, argv); + return cmd_move_in_direction(WLR_DIRECTION_RIGHT, --argc, ++argv); } else if (strcasecmp(argv[0], "up") == 0) { - return cmd_move_in_direction(WLR_DIRECTION_UP, argc, argv); + return cmd_move_in_direction(WLR_DIRECTION_UP, --argc, ++argv); } else if (strcasecmp(argv[0], "down") == 0) { - return cmd_move_in_direction(WLR_DIRECTION_DOWN, argc, argv); - } else if ((strcasecmp(argv[0], "container") == 0 - || strcasecmp(argv[0], "window") == 0) || - (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2 - && (strcasecmp(argv[1], "container") == 0 - || strcasecmp(argv[1], "window") == 0))) { - return cmd_move_container(argc, argv); - } else if (strcasecmp(argv[0], "workspace") == 0) { + return cmd_move_in_direction(WLR_DIRECTION_DOWN, --argc, ++argv); + } else if (strcasecmp(argv[0], "workspace") == 0 && argc >= 2 + && (strcasecmp(argv[1], "to") == 0 || + strcasecmp(argv[1], "output") == 0)) { + argc -= 2; argv += 2; return cmd_move_workspace(argc, argv); - } else if (strcasecmp(argv[0], "scratchpad") == 0 - || (strcasecmp(argv[0], "to") == 0 && argc == 2 - && strcasecmp(argv[1], "scratchpad") == 0)) { - return cmd_move_to_scratchpad(); - } else if (strcasecmp(argv[0], "position") == 0) { - return cmd_move_to_position(argc, argv); - } else if (strcasecmp(argv[0], "absolute") == 0) { - return cmd_move_to_position(argc, argv); - } else { - return cmd_results_new(CMD_INVALID, expected_syntax); } - return cmd_results_new(CMD_SUCCESS, NULL); + + bool no_auto_back_and_forth = false; + if (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { + no_auto_back_and_forth = true; + --argc; ++argv; + } + + if (strcasecmp(argv[0], "window") == 0 || + strcasecmp(argv[0], "container") == 0) { + --argc; ++argv; + } + + if (strcasecmp(argv[0], "to") == 0) { + --argc; ++argv; + } + + if (!argc) { + return cmd_results_new(CMD_INVALID, expected_full_syntax); + } + + // Only `move [window|container] [to] workspace` supports + // `--no-auto-back-and-forth` so treat others as invalid syntax + if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { + return cmd_results_new(CMD_INVALID, expected_full_syntax); + } + + if (strcasecmp(argv[0], "workspace") == 0 || + strcasecmp(argv[0], "output") == 0 || + strcasecmp(argv[0], "mark") == 0) { + return cmd_move_container(no_auto_back_and_forth, argc, argv); + } else if (strcasecmp(argv[0], "scratchpad") == 0) { + return cmd_move_to_scratchpad(); + } else if (strcasecmp(argv[0], "position") == 0 || + (argc > 1 && strcasecmp(argv[0], "absolute") == 0 && + strcasecmp(argv[1], "position") == 0)) { + return cmd_move_to_position(argc, argv); + } + return cmd_results_new(CMD_INVALID, expected_full_syntax); } diff --git a/sway/commands/new_float.c b/sway/commands/new_float.c new file mode 100644 index 000000000..4fedb4cb8 --- /dev/null +++ b/sway/commands/new_float.c @@ -0,0 +1,13 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_new_float(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: new_float is deprecated. " + "Use default_floating_border instead."); + if (config->reading) { + config_add_swaynag_warning("new_float is deprecated. " + "Use default_floating_border instead."); + } + return cmd_default_floating_border(argc, argv); +} diff --git a/sway/commands/new_window.c b/sway/commands/new_window.c new file mode 100644 index 000000000..e8caa4873 --- /dev/null +++ b/sway/commands/new_window.c @@ -0,0 +1,13 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_new_window(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: new_window is deprecated. " + "Use default_border instead."); + if (config->reading) { + config_add_swaynag_warning("new_window is deprecated. " + "Use default_border instead."); + } + return cmd_default_border(argc, argv); +} diff --git a/sway/commands/output.c b/sway/commands/output.c index 40dbf3ca4..3903f10dc 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -18,6 +18,8 @@ static struct cmd_handler output_handlers[] = { { "res", output_cmd_mode }, { "resolution", output_cmd_mode }, { "scale", output_cmd_scale }, + { "subpixel", output_cmd_subpixel }, + { "toggle", output_cmd_toggle }, { "transform", output_cmd_transform }, }; @@ -67,6 +69,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { config->handler_context.leftovers.argc = 0; config->handler_context.leftovers.argv = NULL; + bool background = output->background; + output = store_output_config(output); // If reloading, the output configs will be applied after reading the @@ -74,6 +78,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { // workspace name is not given to re-enabled outputs. if (!config->reloading) { apply_output_config_to_outputs(output); + if (background) { + spawn_swaybg(); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 5a15ed0fc..054fb707e 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -20,6 +20,16 @@ static const char *bg_options[] = { "tile", }; +static bool validate_color(const char *color) { + if (strlen(color) != 7 || color[0] != '#') { + return false; + } + + char *ptr = NULL; + strtol(&color[1], &ptr, 16); + return *ptr == '\0'; +} + struct cmd_results *output_cmd_background(int argc, char **argv) { if (!config->handler_context.output_config) { return cmd_results_new(CMD_FAILURE, "Missing output config"); @@ -36,6 +46,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { struct output_config *output = config->handler_context.output_config; if (strcasecmp(argv[1], "solid_color") == 0) { + if (!validate_color(argv[0])) { + return cmd_results_new(CMD_INVALID, + "Colors should be of the form #RRGGBB"); + } output->background = strdup(argv[0]); output->background_option = strdup("solid_color"); output->background_fallback = NULL; @@ -130,6 +144,11 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { output->background_fallback = NULL; if (argc && *argv[0] == '#') { + if (!validate_color(argv[0])) { + return cmd_results_new(CMD_INVALID, + "fallback color should be of the form #RRGGBB"); + } + output->background_fallback = strdup(argv[0]); argc--; argv++; diff --git a/sway/commands/output/subpixel.c b/sway/commands/output/subpixel.c new file mode 100644 index 000000000..63191ee62 --- /dev/null +++ b/sway/commands/output/subpixel.c @@ -0,0 +1,36 @@ +#include +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *output_cmd_subpixel(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 subpixel argument."); + } + enum wl_output_subpixel subpixel; + + if (strcmp(*argv, "rgb") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; + } else if (strcmp(*argv, "bgr") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; + } else if (strcmp(*argv, "vrgb") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; + } else if (strcmp(*argv, "vbgr") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; + } else if (strcmp(*argv, "none") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } else { + return cmd_results_new(CMD_INVALID, "Invalid output subpixel."); + } + + struct output_config *oc = config->handler_context.output_config; + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + + oc->subpixel = subpixel; + return NULL; +} diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c new file mode 100644 index 000000000..6342d526b --- /dev/null +++ b/sway/commands/output/toggle.c @@ -0,0 +1,37 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *output_cmd_toggle(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + + struct output_config *oc = config->handler_context.output_config; + + 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 == NULL) { + return cmd_results_new(CMD_FAILURE, + "Cannot apply toggle to unknown output %s", oc->name); + } + + oc = find_output_config(sway_output); + + if (!oc || oc->enabled != 0) { + config->handler_context.output_config->enabled = 0; + } else { + config->handler_context.output_config->enabled = 1; + } + + free(oc); + config->handler_context.leftovers.argc = argc; + config->handler_context.leftovers.argv = argv; + return NULL; +} + diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c index 8613a8e78..8e5324adb 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c @@ -12,7 +12,8 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing transform argument."); } enum wl_output_transform transform; - if (strcmp(*argv, "normal") == 0) { + if (strcmp(*argv, "normal") == 0 || + strcmp(*argv, "0") == 0) { transform = WL_OUTPUT_TRANSFORM_NORMAL; } else if (strcmp(*argv, "90") == 0) { transform = WL_OUTPUT_TRANSFORM_90; diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 88377b092..3b855fdf7 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -9,6 +9,7 @@ #include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" +#include "sway/tree/root.h" static const char expected_syntax[] = "Expected 'rename workspace to ' or " @@ -89,6 +90,9 @@ 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; diff --git a/sway/commands/resize.c b/sway/commands/resize.c index c9261535a..440937f0d 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -66,45 +66,6 @@ static int parse_resize_amount(int argc, char **argv, return 2; } -static void calculate_constraints(int *min_width, int *max_width, - int *min_height, int *max_height) { - struct sway_container *con = config->handler_context.container; - - if (config->floating_minimum_width == -1) { // no minimum - *min_width = 0; - } else if (config->floating_minimum_width == 0) { // automatic - *min_width = 75; - } else { - *min_width = config->floating_minimum_width; - } - - if (config->floating_minimum_height == -1) { // no minimum - *min_height = 0; - } else if (config->floating_minimum_height == 0) { // automatic - *min_height = 50; - } else { - *min_height = config->floating_minimum_height; - } - - if (config->floating_maximum_width == -1 || - container_is_scratchpad_hidden(con)) { // no max - *max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - *max_width = con->workspace->width; - } else { - *max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1 || - container_is_scratchpad_hidden(con)) { // no max - *max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - *max_height = con->workspace->height; - } else { - *max_height = config->floating_maximum_height; - } -} - static uint32_t parse_resize_axis(const char *axis) { if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { return AXIS_HORIZONTAL; @@ -258,7 +219,8 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, // Make sure we're not adjusting beyond floating min/max size int min_width, max_width, min_height, max_height; - calculate_constraints(&min_width, &max_width, &min_height, &max_height); + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); if (con->width + grow_width < min_width) { grow_width = min_width - con->width; } else if (con->width + grow_width > max_width) { @@ -383,7 +345,8 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, static struct cmd_results *resize_set_floating(struct sway_container *con, struct resize_amount *width, struct resize_amount *height) { int min_width, max_width, min_height, max_height, grow_width = 0, grow_height = 0; - calculate_constraints(&min_width, &max_width, &min_height, &max_height); + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); if (width->amount) { switch (width->unit) { diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 71afa306d..34871bc6a 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -12,6 +12,11 @@ static void scratchpad_toggle_auto(void) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + sway_log(SWAY_DEBUG, + "No focused workspace to toggle scratchpad windows on"); + return; + } // If the focus is in a floating split container, // operate on the split container instead of the child. diff --git a/sway/commands/split.c b/sway/commands/split.c index e96707228..8702f39e6 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -13,7 +13,8 @@ static struct cmd_results *do_split(int layout) { struct sway_container *con = config->handler_context.container; struct sway_workspace *ws = config->handler_context.workspace; if (con) { - if (container_is_scratchpad_hidden(con)) { + if (container_is_scratchpad_hidden(con) && + con->fullscreen_mode != FULLSCREEN_GLOBAL) { return cmd_results_new(CMD_FAILURE, "Cannot split a hidden scratchpad container"); } diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 5fde81299..b911b2f61 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -184,6 +184,10 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *current = seat_get_focused_workspace(seat); + if (!current) { + return cmd_results_new(CMD_FAILURE, "No workspace to switch from"); + } + struct sway_workspace *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { @@ -222,6 +226,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } free(name); } + if (!ws) { + return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); + } workspace_switch(ws, no_auto_back_and_forth); seat_consider_warp_to_focus(seat); } diff --git a/sway/config.c b/sway/config.c index 206ca95cb..4f92b403d 100644 --- a/sway/config.c +++ b/sway/config.c @@ -33,6 +33,24 @@ struct sway_config *config = NULL; +static struct xkb_state *keysym_translation_state_create( + struct xkb_rule_names rules) { + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( + context, + &rules, + XKB_KEYMAP_COMPILE_NO_FLAGS); + + xkb_context_unref(context); + return xkb_state_new(xkb_keymap); +} + +static void keysym_translation_state_destroy( + struct xkb_state *state) { + xkb_keymap_unref(xkb_state_get_keymap(state)); + xkb_state_unref(state); +} + static void free_mode(struct sway_mode *mode) { if (!mode) { return; @@ -56,6 +74,12 @@ static void free_mode(struct sway_mode *mode) { } list_free(mode->mouse_bindings); } + if (mode->switch_bindings) { + for (int i = 0; i < mode->switch_bindings->length; i++) { + free_switch_binding(mode->switch_bindings->items[i]); + } + list_free(mode->switch_bindings); + } free(mode); } @@ -98,12 +122,21 @@ void free_config(struct sway_config *config) { } list_free(config->output_configs); } + if (config->swaybg_client != NULL) { + wl_client_destroy(config->swaybg_client); + } if (config->input_configs) { for (int i = 0; i < config->input_configs->length; i++) { free_input_config(config->input_configs->items[i]); } list_free(config->input_configs); } + if (config->input_type_configs) { + for (int i = 0; i < config->input_type_configs->length; i++) { + free_input_config(config->input_type_configs->items[i]); + } + list_free(config->input_type_configs); + } if (config->seat_configs) { for (int i = 0; i < config->seat_configs->length; i++) { free_seat_config(config->seat_configs->items[i]); @@ -131,6 +164,7 @@ void free_config(struct sway_config *config) { free(config->swaynag_command); free((char *)config->current_config_path); free((char *)config->current_config); + keysym_translation_state_destroy(config->keysym_translation_state); free(config); } @@ -166,15 +200,13 @@ static void set_color(float dest[static 4], uint32_t color) { static void config_defaults(struct sway_config *config) { if (!(config->swaynag_command = strdup("swaynag"))) goto cleanup; - config->swaynag_config_errors = (struct swaynag_instance){ - .args = "--type error " + config->swaynag_config_errors = (struct swaynag_instance){0}; + config->swaynag_config_errors.args = "--type error " "--message 'There are errors in your config file' " "--detailed-message " - "--button 'Exit sway' 'swaymsg exit' " - "--button 'Reload sway' 'swaymsg reload'", - .pid = -1, - .detailed = true, - }; + "--button-no-terminal 'Exit sway' 'swaymsg exit' " + "--button-no-terminal 'Reload sway' 'swaymsg reload'"; + config->swaynag_config_errors.detailed = true; if (!(config->symbols = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup; @@ -182,10 +214,12 @@ static void config_defaults(struct sway_config *config) { if (!(config->workspace_configs = create_list())) goto cleanup; if (!(config->criteria = create_list())) goto cleanup; if (!(config->no_focus = create_list())) goto cleanup; - if (!(config->input_configs = create_list())) goto cleanup; if (!(config->seat_configs = create_list())) goto cleanup; if (!(config->output_configs = create_list())) goto cleanup; + if (!(config->input_type_configs = create_list())) goto cleanup; + if (!(config->input_configs = create_list())) goto cleanup; + if (!(config->cmd_queue = create_list())) goto cleanup; if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) @@ -195,6 +229,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; 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; list_add(config->modes, config->current_mode); config->floating_mod = 0; @@ -261,6 +296,7 @@ static void config_defaults(struct sway_config *config) { config->floating_border_thickness = 2; config->hide_edge_borders = E_NONE; config->saved_edge_borders = E_NONE; + config->hide_lone_tab = false; // border colors set_color(config->border_colors.focused.border, 0x4C7899); @@ -300,6 +336,11 @@ static void config_defaults(struct sway_config *config) { if (!(config->feature_policies = create_list())) goto cleanup; if (!(config->ipc_policies = create_list())) goto cleanup; + // The keysym to keycode translation + struct xkb_rule_names rules = {0}; + config->keysym_translation_state = + keysym_translation_state_create(rules); + return; cleanup: sway_abort("Unable to allocate config structures"); @@ -400,10 +441,13 @@ bool load_main_config(const char *file, bool is_active, bool validating) { config->reloading = true; config->active = true; - swaynag_kill(&old_config->swaynag_config_errors); - memcpy(&config->swaynag_config_errors, - &old_config->swaynag_config_errors, - sizeof(struct swaynag_instance)); + if (old_config->swaybg_client != NULL) { + wl_client_destroy(old_config->swaybg_client); + } + + if (old_config->swaynag_config_errors.client != NULL) { + wl_client_destroy(old_config->swaynag_config_errors.client); + } input_manager_reset_all_inputs(); } @@ -472,9 +516,10 @@ bool load_main_config(const char *file, bool is_active, bool validating) { if (is_active) { reset_outputs(); + spawn_swaybg(); config->reloading = false; - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } @@ -549,43 +594,34 @@ static bool load_include_config(const char *path, const char *parent_dir, return true; } -bool load_include_configs(const char *path, struct sway_config *config, +void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag) { char *wd = getcwd(NULL, 0); char *parent_path = strdup(config->current_config_path); const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { - free(parent_path); - free(wd); - return false; + sway_log(SWAY_ERROR, "failed to change working directory"); + goto cleanup; } wordexp_t p; - - if (wordexp(path, &p, 0) != 0) { - free(parent_path); - free(wd); - return false; + if (wordexp(path, &p, 0) == 0) { + char **w = p.we_wordv; + size_t i; + for (i = 0; i < p.we_wordc; ++i) { + load_include_config(w[i], parent_dir, config, swaynag); + } + wordfree(&p); } - char **w = p.we_wordv; - size_t i; - for (i = 0; i < p.we_wordc; ++i) { - load_include_config(w[i], parent_dir, config, swaynag); - } - free(parent_path); - wordfree(&p); - - // restore wd + // Attempt to restore working directory before returning. if (chdir(wd) < 0) { - free(wd); - sway_log(SWAY_ERROR, "failed to restore working directory"); - return false; + sway_log(SWAY_ERROR, "failed to change working directory"); } - +cleanup: + free(parent_path); free(wd); - return true; } void run_deferred_commands(void) { @@ -929,3 +965,53 @@ void config_update_font_height(bool recalculate) { arrange_root(); } } + +static void translate_binding_list(list_t *bindings, list_t *bindsyms, + list_t *bindcodes) { + for (int i = 0; i < bindings->length; ++i) { + struct sway_binding *binding = bindings->items[i]; + translate_binding(binding); + + switch (binding->type) { + case BINDING_KEYSYM: + binding_add_translated(binding, bindsyms); + break; + case BINDING_KEYCODE: + binding_add_translated(binding, bindcodes); + break; + default: + sway_assert(false, "unexpected translated binding type: %d", + binding->type); + break; + } + + } +} + +void translate_keysyms(struct input_config *input_config) { + keysym_translation_state_destroy(config->keysym_translation_state); + + struct xkb_rule_names rules = {0}; + input_config_fill_rule_names(input_config, &rules); + config->keysym_translation_state = + keysym_translation_state_create(rules); + + for (int i = 0; i < config->modes->length; ++i) { + struct sway_mode *mode = config->modes->items[i]; + + list_t *bindsyms = create_list(); + list_t *bindcodes = create_list(); + + translate_binding_list(mode->keysym_bindings, bindsyms, bindcodes); + translate_binding_list(mode->keycode_bindings, bindsyms, bindcodes); + + list_free(mode->keysym_bindings); + list_free(mode->keycode_bindings); + + mode->keysym_bindings = bindsyms; + mode->keycode_bindings = bindcodes; + } + + sway_log(SWAY_DEBUG, "Translated keysyms using config for device '%s'", + input_config->identifier); +} diff --git a/sway/config/bar.c b/sway/config/bar.c index 2e28fa1ef..4ab98ff15 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -1,15 +1,15 @@ #define _POSIX_C_SOURCE 200809L -#include +#include #include +#include #include -#include -#include +#include +#include +#include #include #include -#include -#include -#include -#include +#include +#include #include "sway/config.h" #include "sway/input/keyboard.h" #include "sway/output.h" @@ -17,17 +17,7 @@ #include "list.h" #include "log.h" #include "stringop.h" - -static void terminate_swaybar(pid_t pid) { - sway_log(SWAY_DEBUG, "Terminating swaybar %d", pid); - int ret = kill(-pid, SIGTERM); - if (ret != 0) { - sway_log_errno(SWAY_ERROR, "Unable to terminate swaybar %d", pid); - } else { - int status; - waitpid(pid, &status, 0); - } -} +#include "util.h" void free_bar_binding(struct bar_binding *binding) { if (!binding) { @@ -54,8 +44,8 @@ void free_bar_config(struct bar_config *bar) { } list_free(bar->bindings); list_free_items_and_destroy(bar->outputs); - if (bar->pid != 0) { - terminate_swaybar(bar->pid); + if (bar->client != NULL) { + wl_client_destroy(bar->client); } free(bar->colors.background); free(bar->colors.statusline); @@ -110,7 +100,6 @@ struct bar_config *default_bar_config(void) { bar->strip_workspace_name = false; bar->binding_mode_indicator = true; bar->verbose = false; - bar->pid = 0; bar->modifier = get_modifier_mask_by_name("Mod4"); bar->status_padding = 1; bar->status_edge_padding = 3; @@ -190,63 +179,84 @@ cleanup: return NULL; } +static void handle_swaybar_client_destroy(struct wl_listener *listener, + void *data) { + struct bar_config *bar = wl_container_of(listener, bar, client_destroy); + wl_list_remove(&bar->client_destroy.link); + wl_list_init(&bar->client_destroy.link); + bar->client = NULL; +} + static void invoke_swaybar(struct bar_config *bar) { - // Pipe to communicate errors - int filedes[2]; - if (pipe(filedes) == -1) { - sway_log(SWAY_ERROR, "Pipe setup failed! Cannot fork into bar"); + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { return; } - bar->pid = fork(); - if (bar->pid == 0) { - setpgid(0, 0); - close(filedes[0]); + bar->client = wl_client_create(server.wl_display, sockets[0]); + if (bar->client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return; + } + + bar->client_destroy.notify = handle_swaybar_client_destroy; + wl_client_add_destroy_listener(bar->client, &bar->client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); + return; + } else if (pid == 0) { + // Remove the SIGUSR1 handler that wlroots adds for xwayland sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); - // run custom swaybar - size_t len = snprintf(NULL, 0, "%s -b %s", - bar->swaybar_command ? bar->swaybar_command : "swaybar", - bar->id); - char *command = malloc(len + 1); - if (!command) { - const char msg[] = "Unable to allocate swaybar command string"; - size_t msg_len = sizeof(msg); - if (write(filedes[1], &msg_len, sizeof(size_t))) {}; - if (write(filedes[1], msg, msg_len)) {}; - close(filedes[1]); - exit(1); + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + _exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + // run custom swaybar + char *const cmd[] = { + bar->swaybar_command ? bar->swaybar_command : "swaybar", + "-b", bar->id, NULL}; + execvp(cmd[0], cmd); + _exit(EXIT_FAILURE); } - snprintf(command, len + 1, "%s -b %s", - bar->swaybar_command ? bar->swaybar_command : "swaybar", - bar->id); - char *const cmd[] = { "sh", "-c", command, NULL, }; - close(filedes[1]); - execvp(cmd[0], cmd); - exit(1); + _exit(EXIT_SUCCESS); } - sway_log(SWAY_DEBUG, "Spawned swaybar %d", bar->pid); - close(filedes[0]); - size_t len; - if (read(filedes[1], &len, sizeof(size_t)) == sizeof(size_t)) { - char *buf = malloc(len); - if(!buf) { - sway_log(SWAY_ERROR, "Cannot allocate error string"); - return; - } - if (read(filedes[1], buf, len)) { - sway_log(SWAY_ERROR, "%s", buf); - } - free(buf); + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return; } - close(filedes[1]); + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return; + } + + sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); + return; } void load_swaybar(struct bar_config *bar) { - if (bar->pid != 0) { - terminate_swaybar(bar->pid); + if (bar->client != NULL) { + wl_client_destroy(bar->client); } sway_log(SWAY_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); invoke_swaybar(bar); diff --git a/sway/config/input.c b/sway/config/input.c index 63c28635b..9c533e710 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -18,6 +18,7 @@ struct input_config *new_input_config(const char* identifier) { return NULL; } + input->input_type = NULL; input->tap = INT_MIN; input->tap_button_map = INT_MIN; input->drag = INT_MIN; @@ -140,6 +141,30 @@ static void merge_wildcard_on_all(struct input_config *wildcard) { merge_input_config(ic, wildcard); } } + + for (int i = 0; i < config->input_type_configs->length; i++) { + struct input_config *ic = config->input_type_configs->items[i]; + if (strcmp(wildcard->identifier, ic->identifier) != 0) { + sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier); + merge_input_config(ic, wildcard); + } + } +} + +static void merge_type_on_existing(struct input_config *type_wildcard) { + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (ic->input_type == NULL) { + continue; + } + + if (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) { + sway_log(SWAY_DEBUG, "Merging %s top of %s", + type_wildcard->identifier, + ic->identifier); + merge_input_config(ic, type_wildcard); + } + } } struct input_config *store_input_config(struct input_config *ic) { @@ -148,11 +173,19 @@ struct input_config *store_input_config(struct input_config *ic) { merge_wildcard_on_all(ic); } - int i = list_seq_find(config->input_configs, input_identifier_cmp, + list_t *config_list = NULL; + if (strncmp(ic->identifier, "type:", 5) == 0) { + config_list = config->input_type_configs; + merge_type_on_existing(ic); + } else { + config_list = config->input_configs; + } + + int i = list_seq_find(config_list, input_identifier_cmp, ic->identifier); if (i >= 0) { sway_log(SWAY_DEBUG, "Merging on top of existing input config"); - struct input_config *current = config->input_configs->items[i]; + struct input_config *current = config_list->items[i]; merge_input_config(current, ic); free_input_config(ic); ic = current; @@ -167,7 +200,7 @@ struct input_config *store_input_config(struct input_config *ic) { free_input_config(ic); ic = current; } - list_add(config->input_configs, ic); + list_add(config_list, ic); } else { // New wildcard config. Just add it sway_log(SWAY_DEBUG, "Adding input * config"); @@ -179,6 +212,15 @@ struct input_config *store_input_config(struct input_config *ic) { return ic; } +void input_config_fill_rule_names(struct input_config *ic, + struct xkb_rule_names *rules) { + rules->layout = ic->xkb_layout; + rules->model = ic->xkb_model; + rules->options = ic->xkb_options; + rules->rules = ic->xkb_rules; + rules->variant = ic->xkb_variant; +} + void free_input_config(struct input_config *ic) { if (!ic) { return; diff --git a/sway/config/output.c b/sway/config/output.c index e7fbad833..f8e78fd15 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,6 +1,5 @@ #define _POSIX_C_SOURCE 200809L #include -#include #include #include #include @@ -8,10 +7,11 @@ #include #include #include -#include "log.h" #include "sway/config.h" #include "sway/output.h" #include "sway/tree/root.h" +#include "log.h" +#include "util.h" int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; @@ -43,6 +43,7 @@ struct output_config *new_output_config(const char *name) { oc->x = oc->y = -1; oc->scale = -1; oc->transform = -1; + oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; return oc; } @@ -65,6 +66,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->scale != -1) { dst->scale = src->scale; } + if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { + dst->subpixel = src->subpixel; + } if (src->refresh_rate != -1) { dst->refresh_rate = src->refresh_rate; } @@ -98,10 +102,67 @@ static void merge_wildcard_on_all(struct output_config *wildcard) { } } +static void merge_id_on_name(struct output_config *oc) { + char *id_on_name = NULL; + char id[128]; + char *name = NULL; + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + name = output->wlr_output->name; + output_get_identifier(id, sizeof(id), output); + if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { + size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; + id_on_name = malloc(length); + if (!id_on_name) { + sway_log(SWAY_ERROR, "Failed to allocate id on name string"); + return; + } + snprintf(id_on_name, length, "%s on %s", id, name); + break; + } + } + + if (!id_on_name) { + return; + } + + int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); + if (i >= 0) { + sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); + merge_output_config(config->output_configs->items[i], oc); + } else { + // If both a name and identifier config, exist generate an id on name + int ni = list_seq_find(config->output_configs, output_name_cmp, name); + int ii = list_seq_find(config->output_configs, output_name_cmp, id); + if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) + || (ii >= 0 && strcmp(oc->name, name) == 0)) { + struct output_config *ion_oc = new_output_config(id_on_name); + if (ni >= 0) { + merge_output_config(ion_oc, config->output_configs->items[ni]); + } + if (ii >= 0) { + merge_output_config(ion_oc, config->output_configs->items[ii]); + } + merge_output_config(ion_oc, 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)", 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); + } + } + free(id_on_name); +} + struct output_config *store_output_config(struct output_config *oc) { bool wildcard = strcmp(oc->name, "*") == 0; if (wildcard) { merge_wildcard_on_all(oc); + } else { + merge_id_on_name(oc); } int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); @@ -130,10 +191,10 @@ 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 transform %d) (bg %s %s) (dpms %d)", + "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, - oc->x, oc->y, oc->scale, oc->transform, oc->background, - oc->background_option, oc->dpms_state); + oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), + oc->transform, oc->background, oc->background_option, oc->dpms_state); return oc; } @@ -166,91 +227,6 @@ static bool set_mode(struct wlr_output *output, int width, int height, return wlr_output_set_mode(output, best); } -static void handle_swaybg_client_destroy(struct wl_listener *listener, - void *data) { - struct sway_output *output = - wl_container_of(listener, output, swaybg_client_destroy); - wl_list_remove(&output->swaybg_client_destroy.link); - wl_list_init(&output->swaybg_client_destroy.link); - output->swaybg_client = NULL; -} - -static bool set_cloexec(int fd, bool cloexec) { - int flags = fcntl(fd, F_GETFD); - if (flags == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - if (cloexec) { - flags = flags | FD_CLOEXEC; - } else { - flags = flags & ~FD_CLOEXEC; - } - if (fcntl(fd, F_SETFD, flags) == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - return true; -} - -static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { - sway_log_errno(SWAY_ERROR, "socketpair failed"); - return false; - } - if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { - return false; - } - - output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); - if (output->swaybg_client == NULL) { - sway_log_errno(SWAY_ERROR, "wl_client_create failed"); - return false; - } - - output->swaybg_client_destroy.notify = handle_swaybg_client_destroy; - wl_client_add_destroy_listener(output->swaybg_client, - &output->swaybg_client_destroy); - - pid_t pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - return false; - } else if (pid == 0) { - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!set_cloexec(sockets[1], false)) { - exit(EXIT_FAILURE); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - execvp(cmd[0], cmd); - sway_log_errno(SWAY_ERROR, "execvp failed"); - exit(EXIT_FAILURE); - } - exit(EXIT_SUCCESS); - } - - if (close(sockets[1]) != 0) { - sway_log_errno(SWAY_ERROR, "close failed"); - return false; - } - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return false; - } - - return true; -} - bool apply_output_config(struct output_config *oc, struct sway_output *output) { if (output == root->noop_output) { return false; @@ -306,6 +282,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { sway_log(SWAY_DEBUG, "Set %s scale to %f", oc->name, oc->scale); wlr_output_set_scale(wlr_output, oc->scale); } + + 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); + output_damage_whole(output); + } + if (oc && oc->transform >= 0) { sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); wlr_output_set_transform(wlr_output, oc->transform); @@ -319,25 +303,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } - if (output->swaybg_client != NULL) { - wl_client_destroy(output->swaybg_client); - } - if (oc && oc->background && config->swaybg_command) { - sway_log(SWAY_DEBUG, "Setting background for output %s to %s", - wlr_output->name, oc->background); + // 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; + wlr_output_transformed_resolution(wlr_output, + &output->width, &output->height); - char *const cmd[] = { - config->swaybg_command, - wlr_output->name, - oc->background, - oc->background_option, - oc->background_fallback ? oc->background_fallback : NULL, - NULL, - }; - if (!spawn_swaybg(output, cmd)) { - return false; - } - } if (oc && oc->dpms_state == DPMS_OFF) { sway_log(SWAY_DEBUG, "Turning off screen"); @@ -359,6 +332,8 @@ static void default_output_config(struct output_config *oc, } oc->x = oc->y = -1; oc->scale = 1; + struct sway_output *output = wlr_output->data; + oc->subpixel = output->detected_subpixel; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->dpms_state = DPMS_ON; } @@ -366,35 +341,51 @@ static void default_output_config(struct output_config *oc, static struct output_config *get_output_config(char *identifier, struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; + + struct output_config *oc_id_on_name = NULL; struct output_config *oc_name = NULL; - int i = list_seq_find(config->output_configs, output_name_cmp, name); - if (i >= 0) { - oc_name = config->output_configs->items[i]; - } - struct output_config *oc_id = NULL; - i = list_seq_find(config->output_configs, output_name_cmp, identifier); + + size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; + char *id_on_name = malloc(length); + snprintf(id_on_name, length, "%s on %s", identifier, name); + int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); if (i >= 0) { - oc_id = config->output_configs->items[i]; + oc_id_on_name = config->output_configs->items[i]; + } else { + i = list_seq_find(config->output_configs, output_name_cmp, name); + if (i >= 0) { + oc_name = config->output_configs->items[i]; + } + + i = list_seq_find(config->output_configs, output_name_cmp, identifier); + if (i >= 0) { + oc_id = config->output_configs->items[i]; + } } - struct output_config *result = result = new_output_config("temp"); + struct output_config *result = new_output_config("temp"); if (config->reloading) { default_output_config(result, sway_output->wlr_output); } - if (oc_name && oc_id) { + if (oc_id_on_name) { + // Already have an identifier on name config, use that + free(result->name); + result->name = strdup(id_on_name); + merge_output_config(result, oc_id_on_name); + } else if (oc_name && oc_id) { // Generate a config named ` on ` which contains a // merged copy of the identifier on name. This will make sure that both // identifier and name configs are respected, with identifier getting // priority - size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; - char *temp = malloc(length); - snprintf(temp, length, "%s on %s", identifier, name); + struct output_config *temp = new_output_config(id_on_name); + merge_output_config(temp, oc_name); + merge_output_config(temp, oc_id); + list_add(config->output_configs, temp); free(result->name); - result->name = temp; - merge_output_config(result, oc_name); - merge_output_config(result, oc_id); + result->name = strdup(id_on_name); + merge_output_config(result, temp); sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" @@ -412,24 +403,32 @@ static struct output_config *get_output_config(char *identifier, free(result->name); result->name = strdup(identifier); merge_output_config(result, oc_id); - } else if (config->reloading) { - // Neither config exists, but we need to reset the output so create a - // default config for the output and if a wildcard config exists, merge - // that on top - free(result->name); - result->name = strdup("*"); + } else { i = list_seq_find(config->output_configs, output_name_cmp, "*"); if (i >= 0) { + // No name or identifier config, but there is a wildcard config + free(result->name); + result->name = strdup("*"); merge_output_config(result, config->output_configs->items[i]); + } else if (!config->reloading) { + // No name, identifier, or wildcard config. Since we are not + // reloading with defaults, the output config will be empty, so + // just return NULL + free_output_config(result); + result = NULL; } - } else { - free_output_config(result); - result = NULL; } + free(id_on_name); return result; } +struct output_config *find_output_config(struct sway_output *output) { + char id[128]; + output_get_identifier(id, sizeof(id), output); + return get_output_config(id, output); +} + void apply_output_config_to_outputs(struct output_config *oc) { // Try to find the output container and apply configuration now. If // this is during startup then there will be no container and config @@ -441,14 +440,12 @@ void apply_output_config_to_outputs(struct output_config *oc) { char *name = sway_output->wlr_output->name; output_get_identifier(id, sizeof(id), sway_output); if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { - struct output_config *current = new_output_config(oc->name); - merge_output_config(current, oc); - if (wildcard) { - struct output_config *tmp = get_output_config(id, sway_output); - if (tmp) { - free_output_config(current); - current = tmp; - } + struct output_config *current = get_output_config(id, sway_output); + if (!current) { + // No stored output config matched, apply oc directly + sway_log(SWAY_DEBUG, "Applying oc directly"); + current = new_output_config(oc->name); + merge_output_config(current, oc); } apply_output_config(current, sway_output); free_output_config(current); @@ -482,3 +479,135 @@ void free_output_config(struct output_config *oc) { free(oc->background_option); free(oc); } + +static void handle_swaybg_client_destroy(struct wl_listener *listener, + void *data) { + struct sway_config *sway_config = + wl_container_of(listener, sway_config, swaybg_client_destroy); + wl_list_remove(&sway_config->swaybg_client_destroy.link); + wl_list_init(&sway_config->swaybg_client_destroy.link); + sway_config->swaybg_client = NULL; +} + +static bool _spawn_swaybg(char **command) { + if (config->swaybg_client != NULL) { + wl_client_destroy(config->swaybg_client); + } + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + return false; + } + + config->swaybg_client = wl_client_create(server.wl_display, sockets[0]); + if (config->swaybg_client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return false; + } + + config->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(config->swaybg_client, + &config->swaybg_client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + return false; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + _exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(command[0], command); + sway_log_errno(SWAY_ERROR, "execvp failed"); + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); + } + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; +} + +bool spawn_swaybg(void) { + if (!config->swaybg_command) { + return true; + } + + size_t length = 2; + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *oc = config->output_configs->items[i]; + if (!oc->background) { + continue; + } + if (strcmp(oc->background_option, "solid_color") == 0) { + length += 4; + } else if (oc->background_fallback) { + length += 8; + } else { + length += 6; + } + } + + char **cmd = calloc(1, sizeof(char **) * length); + if (!cmd) { + sway_log(SWAY_ERROR, "Failed to allocate spawn_swaybg command"); + return false; + } + + size_t i = 0; + cmd[i++] = config->swaybg_command; + for (int j = 0; j < config->output_configs->length; j++) { + struct output_config *oc = config->output_configs->items[j]; + if (!oc->background) { + continue; + } + if (strcmp(oc->background_option, "solid_color") == 0) { + cmd[i++] = "-o"; + cmd[i++] = oc->name; + cmd[i++] = "-c"; + cmd[i++] = oc->background; + } else { + cmd[i++] = "-o"; + cmd[i++] = oc->name; + cmd[i++] = "-i"; + cmd[i++] = oc->background; + cmd[i++] = "-m"; + cmd[i++] = oc->background_option; + if (oc->background_fallback) { + cmd[i++] = "-c"; + cmd[i++] = oc->background_fallback; + } + } + assert(i <= length); + } + + for (size_t k = 0; k < i; k++) { + sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%zd] = %s", k, cmd[k]); + } + + bool result = _spawn_swaybg(cmd); + free(cmd); + return result; +} diff --git a/sway/criteria.c b/sway/criteria.c index 48ef39df4..11b41f356 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -16,7 +16,8 @@ #include "config.h" bool criteria_is_empty(struct criteria *criteria) { - return !criteria->title + return !criteria->autofail + && !criteria->title && !criteria->shell && !criteria->app_id && !criteria->con_mark @@ -98,6 +99,10 @@ static void find_urgent_iterator(struct sway_container *con, void *data) { static bool criteria_matches_view(struct criteria *criteria, struct sway_view *view) { + if (criteria->autofail) { + return false; + } + if (criteria->title) { const char *title = view_get_title(view); if (!title || regex_cmp(title, criteria->title) != 0) { @@ -208,7 +213,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->workspace) { struct sway_workspace *ws = view->container->workspace; - if (!ws || strcmp(ws->name, criteria->workspace) != 0) { + if (!ws || regex_cmp(ws->name, criteria->workspace) != 0) { return false; } } @@ -366,50 +371,66 @@ static enum criteria_token token_from_name(char *name) { * using criteria via IPC. Using __focused__ in config is not useful because * criteria is only executed once per view. */ -static char *get_focused_prop(enum criteria_token token) { +static char *get_focused_prop(enum criteria_token token, bool *autofail) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); - if (!focus || !focus->view) { - return NULL; - } - struct sway_view *view = focus->view; + struct sway_view *view = focus ? focus->view : NULL; const char *value = NULL; switch (token) { case T_APP_ID: - value = view_get_app_id(view); + *autofail = true; + if (view) { + value = view_get_app_id(view); + } break; case T_SHELL: - value = view_get_shell(view); + *autofail = true; + if (view) { + value = view_get_shell(view); + } break; case T_TITLE: - value = view_get_title(view); + *autofail = true; + if (view) { + value = view_get_title(view); + } break; case T_WORKSPACE: - if (focus->workspace) { + *autofail = true; + if (focus && focus->workspace) { value = focus->workspace->name; } break; case T_CON_ID: - if (view->container == NULL) { - return NULL; + *autofail = true; + if (view && view->container) { + size_t id = view->container->node.id; + size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; + char *id_str = malloc(id_size); + snprintf(id_str, id_size, "%zu", id); + value = id_str; } - size_t id = view->container->node.id; - size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; - char *id_str = malloc(id_size); - snprintf(id_str, id_size, "%zu", id); - value = id_str; break; #if HAVE_XWAYLAND case T_CLASS: - value = view_get_class(view); + *autofail = true; + if (view) { + value = view_get_class(view); + } break; case T_INSTANCE: - value = view_get_instance(view); + *autofail = true; + if (view) { + value = view_get_instance(view); + } break; case T_WINDOW_ROLE: - value = view_get_window_role(view); + *autofail = true; + if (view) { + value = view_get_window_role(view); + } break; case T_WINDOW_TYPE: // These do not support __focused__ case T_ID: @@ -419,6 +440,7 @@ static char *get_focused_prop(enum criteria_token token) { case T_TILING: case T_URGENT: case T_INVALID: + *autofail = false; break; } if (value) { @@ -439,7 +461,12 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { char *effective_value = NULL; if (value && strcmp(value, "__focused__") == 0) { - effective_value = get_focused_prop(token); + bool autofail = false; + effective_value = get_focused_prop(token, &autofail); + if (!effective_value && autofail) { + criteria->autofail = true; + return true; + } } else if (value) { effective_value = strdup(value); } @@ -515,7 +542,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { } break; case T_WORKSPACE: - criteria->workspace = strdup(effective_value); + generate_regex(&criteria->workspace, effective_value); break; case T_INVALID: break; @@ -624,8 +651,8 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { in_quotes = false; } unescape(value); + sway_log(SWAY_DEBUG, "Found pair: %s=%s", name, value); } - sway_log(SWAY_DEBUG, "Found pair: %s=%s", name, value); if (!parse_token(criteria, name, value)) { *error_arg = error; goto cleanup; diff --git a/sway/debug-tree.c b/sway/debug-tree.c deleted file mode 100644 index 0444bb3fd..000000000 --- a/sway/debug-tree.c +++ /dev/null @@ -1,161 +0,0 @@ -#include -#include -#include -#include "config.h" -#include "sway/debug.h" -#include "sway/input/input-manager.h" -#include "sway/input/seat.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/tree/container.h" -#include "sway/tree/root.h" -#include "sway/tree/workspace.h" -#include "cairo.h" -#include "config.h" -#include "pango.h" - -struct sway_debug debug; - -static const char *layout_to_str(enum sway_container_layout layout) { - switch (layout) { - case L_HORIZ: - return "L_HORIZ"; - case L_VERT: - return "L_VERT"; - case L_STACKED: - return "L_STACKED"; - case L_TABBED: - return "L_TABBED"; - case L_NONE: - return "L_NONE"; - } - return "L_NONE"; -} - -static char *get_string(struct sway_node *node) { - char *buffer = malloc(512); - switch (node->type) { - case N_ROOT: - snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id, - root->width, root->height, root->x, root->y); - break; - case N_OUTPUT: - snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id, - node->sway_output->wlr_output->name, - node->sway_output->width, - node->sway_output->height, - node->sway_output->lx, - node->sway_output->ly); - break; - case N_WORKSPACE: - snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f", - node->id, node->sway_workspace->name, - layout_to_str(node->sway_workspace->layout), - node->sway_workspace->width, node->sway_workspace->height, - node->sway_workspace->x, node->sway_workspace->y); - break; - case N_CONTAINER: - snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f", - node->id, node->sway_container->title, - layout_to_str(node->sway_container->layout), - node->sway_container->width, node->sway_container->height, - node->sway_container->x, node->sway_container->y); - break; - } - return buffer; -} - -static list_t *get_children(struct sway_node *node) { - switch (node->type) { - case N_ROOT: - return root->outputs; - case N_OUTPUT: - return node->sway_output->workspaces; - case N_WORKSPACE: - return node->sway_workspace->tiling; - case N_CONTAINER: - return node->sway_container->children; - } - return NULL; -} - -static int draw_node(cairo_t *cairo, struct sway_node *node, - struct sway_node *focus, int x, int y) { - int text_width, text_height; - char *buffer = get_string(node); - get_text_size(cairo, "monospace", &text_width, &text_height, NULL, - 1, false, buffer); - cairo_save(cairo); - cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); - cairo_set_source_u32(cairo, 0xFFFFFFE0); - cairo_fill(cairo); - int height = text_height; - list_t *children = get_children(node); - if (children) { - for (int i = 0; i < children->length; ++i) { - // This is really dirty - the list contains specific structs but - // we're casting them as nodes. This works because node is the first - // item in each specific struct. This is acceptable because this is - // debug code. - struct sway_node *child = children->items[i]; - if (node_get_parent(child) == node) { - cairo_set_source_u32(cairo, 0x000000FF); - } else { - cairo_set_source_u32(cairo, 0xFF0000FF); - } - height += draw_node(cairo, child, focus, x + 10, y + height); - } - } - cairo_set_source_u32(cairo, 0xFFFFFFE0); - cairo_rectangle(cairo, x, y, 2, height); - cairo_fill(cairo); - cairo_restore(cairo); - cairo_move_to(cairo, x, y); - if (focus == node) { - cairo_set_source_u32(cairo, 0x0000FFFF); - } - pango_printf(cairo, "monospace", 1, false, buffer); - free(buffer); - return height; -} - -void update_debug_tree(void) { - if (!debug.render_tree) { - return; - } - - int width = 640, height = 480; - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - if (output->width > width) { - width = output->width; - } - if (output->height > height) { - height = output->height; - } - } - cairo_surface_t *surface = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *cairo = cairo_create(surface); - PangoContext *pango = pango_cairo_create_context(cairo); - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *focus = seat_get_focus(seat); - - cairo_set_source_u32(cairo, 0x000000FF); - draw_node(cairo, &root->node, focus, 0, 0); - - cairo_surface_flush(surface); - struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); - if (root->debug_tree) { - wlr_texture_destroy(root->debug_tree); - } - unsigned char *data = cairo_image_surface_get_data(surface); - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - struct wlr_texture *texture = wlr_texture_from_pixels(renderer, - WL_SHM_FORMAT_ARGB8888, stride, width, height, data); - root->debug_tree = texture; - cairo_surface_destroy(surface); - g_object_unref(pango); - cairo_destroy(cairo); -} diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index d8dd02403..ec45d80a1 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -6,8 +6,10 @@ 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]; - output_damage_surface(output, lx - output->wlr_output->lx, - ly - output->wlr_output->ly, surface, whole); + 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); } } diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 87b4ef43d..73e46a8f8 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -2,18 +2,24 @@ #include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" +#include "sway/input/seat.h" +#include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/server.h" +static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { + wl_list_remove(&inhibitor->link); + wl_list_remove(&inhibitor->destroy.link); + sway_idle_inhibit_v1_check_active(inhibitor->manager); + free(inhibitor); +} + static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_idle_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, destroy); sway_log(SWAY_DEBUG, "Sway idle inhibitor destroyed"); - wl_list_remove(&inhibitor->link); - wl_list_remove(&inhibitor->destroy.link); - idle_inhibit_v1_check_active(inhibitor->manager); - free(inhibitor); + destroy_inhibitor(inhibitor); } void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { @@ -29,28 +35,94 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { } inhibitor->manager = manager; + inhibitor->mode = INHIBIT_IDLE_APPLICATION; inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); wl_list_insert(&manager->inhibitors, &inhibitor->link); - inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); - idle_inhibit_v1_check_active(manager); + sway_idle_inhibit_v1_check_active(manager); } -void idle_inhibit_v1_check_active( +void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, + enum sway_idle_inhibit_mode mode) { + struct sway_idle_inhibitor_v1 *inhibitor = + calloc(1, sizeof(struct sway_idle_inhibitor_v1)); + if (!inhibitor) { + return; + } + + inhibitor->manager = server.idle_inhibit_manager_v1; + inhibitor->mode = mode; + inhibitor->view = view; + wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); + + inhibitor->destroy.notify = handle_destroy; + wl_signal_add(&view->events.unmap, &inhibitor->destroy); + + sway_idle_inhibit_v1_check_active(inhibitor->manager); +} + +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( + struct sway_view *view) { + struct sway_idle_inhibitor_v1 *inhibitor; + wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, + link) { + if (inhibitor->view == view && + inhibitor->mode != INHIBIT_IDLE_APPLICATION) { + return inhibitor; + } + } + return NULL; +} + +void sway_idle_inhibit_v1_user_inhibitor_destroy( + struct sway_idle_inhibitor_v1 *inhibitor) { + if (!inhibitor) { + return; + } + if (!sway_assert(inhibitor->mode != INHIBIT_IDLE_APPLICATION, + "User should not be able to destroy application inhibitor")) { + return; + } + destroy_inhibitor(inhibitor); +} + +static bool check_active(struct sway_idle_inhibitor_v1 *inhibitor) { + switch (inhibitor->mode) { + case INHIBIT_IDLE_APPLICATION: + // If there is no view associated with the inhibitor, assume visible + return !inhibitor->view || !inhibitor->view->container || + view_is_visible(inhibitor->view); + case INHIBIT_IDLE_FOCUS:; + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_container *con = seat_get_focused_container(seat); + if (con && con->view && con->view == inhibitor->view) { + return true; + } + } + return false; + case INHIBIT_IDLE_FULLSCREEN: + return inhibitor->view->container && + container_is_fullscreen_or_child(inhibitor->view->container) && + view_is_visible(inhibitor->view); + case INHIBIT_IDLE_OPEN: + // Inhibitor is destroyed on unmap so it must be open/mapped + return true; + case INHIBIT_IDLE_VISIBLE: + return view_is_visible(inhibitor->view); + } + return false; +} + +void sway_idle_inhibit_v1_check_active( struct sway_idle_inhibit_manager_v1 *manager) { struct sway_idle_inhibitor_v1 *inhibitor; bool inhibited = false; wl_list_for_each(inhibitor, &manager->inhibitors, link) { - if (!inhibitor->view || !inhibitor->view->container) { - /* Cannot guess if view is visible so assume it is */ - inhibited = true; - break; - } - if (view_is_visible(inhibitor->view)) { - inhibited = true; + if ((inhibited = check_active(inhibitor))) { break; } } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 0767247c4..1667995c7 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -373,12 +373,11 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct sway_seat *seat = input_manager_get_default_seat(); if (seat) { struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (ws != NULL) { output = ws->output; } } - if (!output) { + if (!output || output == root->noop_output) { if (!root->outputs->length) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d7d3fc07e..bf5b284bf 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -42,6 +42,19 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { return NULL; } +struct sway_output *all_output_by_name_or_id(const char *name_or_id) { + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + char identifier[128]; + output_get_identifier(identifier, sizeof(identifier), output); + if (strcasecmp(identifier, name_or_id) == 0 + || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + return output; + } + } + return NULL; +} + /** * Rotate a child's position relative to a parent. The parent size is (pw, ph), * the child position is (*sx, *sy) and its size is (sw, sh). @@ -150,9 +163,9 @@ void output_view_for_each_surface(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.content_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->lx - view->geometry.x, - .oy = view->container->current.content_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->ly - view->geometry.y, .width = view->container->current.content_width, .height = view->container->current.content_height, @@ -169,9 +182,9 @@ void output_view_for_each_popup(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.content_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->lx - view->geometry.x, - .oy = view->container->current.content_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->ly - view->geometry.y, .width = view->container->current.content_width, .height = view->container->current.content_height, @@ -202,8 +215,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output, wl_list_for_each(unmanaged_surface, unmanaged, link) { struct wlr_xwayland_surface *xsurface = unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->wlr_output->lx; - double oy = unmanaged_surface->ly - output->wlr_output->ly; + double ox = unmanaged_surface->lx - output->lx; + double oy = unmanaged_surface->ly - output->ly; output_surface_for_each_surface(output, xsurface->surface, ox, oy, iterator, user_data); @@ -216,8 +229,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output, void *user_data) { struct sway_drag_icon *drag_icon; wl_list_for_each(drag_icon, drag_icons, link) { - double ox = drag_icon->x - output->wlr_output->lx; - double oy = drag_icon->y - output->wlr_output->ly; + double ox = drag_icon->x - output->lx; + double oy = drag_icon->y - output->ly; if (drag_icon->wlr_drag_icon->mapped) { output_surface_for_each_surface(output, @@ -251,14 +264,11 @@ static void output_for_each_surface(struct sway_output *output, }; struct sway_workspace *workspace = output_get_active_workspace(output); - if (!workspace) { - return; - } struct sway_container *fullscreen_con = root->fullscreen_global; - if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { - fullscreen_con = NULL; - } if (!fullscreen_con) { + if (!workspace) { + return; + } fullscreen_con = workspace->current.fullscreen; } if (fullscreen_con) { @@ -381,14 +391,15 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - bool needs_swap; + bool needs_frame; pixman_region32_t damage; pixman_region32_init(&damage); - if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) { + if (!wlr_output_damage_attach_render(output->damage, + &needs_frame, &damage)) { return; } - if (needs_swap) { + if (needs_frame) { output_render(output, &now, &damage); } @@ -450,40 +461,53 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, damage_surface_iterator, &whole); } -static void output_damage_view(struct sway_output *output, - struct sway_view *view, bool whole) { +void output_damage_from_view(struct sway_output *output, + struct sway_view *view) { if (!view_is_visible(view)) { return; } + bool whole = false; output_view_for_each_surface(output, view, damage_surface_iterator, &whole); } -void output_damage_from_view(struct sway_output *output, - struct sway_view *view) { - output_damage_view(output, view, false); -} - // Expecting an unscaled box in layout coordinates void output_damage_box(struct sway_output *output, struct wlr_box *_box) { struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->wlr_output->lx; - box.y -= output->wlr_output->ly; + box.x -= output->lx; + box.y -= output->ly; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); } +static void damage_child_views_iterator(struct sway_container *con, + void *data) { + if (!con->view || !view_is_visible(con->view)) { + return; + } + struct sway_output *output = data; + bool whole = true; + output_view_for_each_surface(output, con->view, damage_surface_iterator, + &whole); +} + void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.x - output->wlr_output->lx - 1, - .y = con->current.y - output->wlr_output->ly - 1, + .x = con->current.x - output->lx - 1, + .y = con->current.y - output->ly - 1, .width = con->current.width + 2, .height = con->current.height + 2, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); + // Damage subsurfaces as well, which may extend outside the box + if (con->view) { + damage_child_views_iterator(con, output); + } else { + container_for_each_child(con, damage_child_views_iterator, output); + } } static void damage_handle_destroy(struct wl_listener *listener, void *data) { @@ -496,8 +520,33 @@ static void damage_handle_destroy(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } +static void update_output_manager_config(struct sway_server *server) { + struct wlr_output_configuration_v1 *config = + wlr_output_configuration_v1_create(); + + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + if (output == root->noop_output) { + continue; + } + 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 + config_head->state.enabled = output->enabled; + if (output_box) { + config_head->state.x = output_box->x; + config_head->state.y = output_box->y; + } + } + + wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); +} + 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; wl_signal_emit(&output->events.destroy, output); if (output->enabled) { @@ -512,15 +561,16 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->present.link); wl_list_remove(&output->damage_destroy.link); wl_list_remove(&output->damage_frame.link); - wl_list_remove(&output->swaybg_client_destroy.link); transaction_commit_dirty(); + + update_output_manager_config(server); } static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); if (!output->configured && !output->enabled) { - struct output_config *oc = output_find_config(output); + struct output_config *oc = find_output_config(output); if (output->wlr_output->current_mode != NULL && (!oc || oc->enabled)) { // We want to enable this output, but it didn't work last time, @@ -538,6 +588,8 @@ static void handle_mode(struct wl_listener *listener, void *data) { arrange_layers(output); arrange_output(output); transaction_commit_dirty(); + + update_output_manager_config(output->server); } static void handle_transform(struct wl_listener *listener, void *data) { @@ -548,6 +600,8 @@ static void handle_transform(struct wl_listener *listener, void *data) { arrange_layers(output); arrange_output(output); transaction_commit_dirty(); + + update_output_manager_config(output->server); } static void update_textures(struct sway_container *con, void *data) { @@ -564,6 +618,8 @@ static void handle_scale(struct wl_listener *listener, void *data) { output_for_each_container(output, update_textures, NULL); arrange_output(output); transaction_commit_dirty(); + + update_output_manager_config(output->server); } static void send_presented_iterator(struct sway_output *output, @@ -619,9 +675,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { 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_list_init(&output->swaybg_client_destroy.link); - struct output_config *oc = output_find_config(output); + struct output_config *oc = find_output_config(output); if (!oc || oc->enabled) { output_enable(output, oc); } else { @@ -629,4 +684,73 @@ void handle_new_output(struct wl_listener *listener, void *data) { } transaction_commit_dirty(); + + update_output_manager_config(server); +} + +void handle_output_manager_apply(struct wl_listener *listener, void *data) { + struct sway_server *server = + wl_container_of(listener, server, output_manager_apply); + struct wlr_output_configuration_v1 *config = data; + + struct wlr_output_configuration_head_v1 *config_head; + // First disable outputs we need to disable + bool ok = true; + wl_list_for_each(config_head, &config->heads, link) { + struct wlr_output *wlr_output = config_head->state.output; + struct sway_output *output = wlr_output->data; + if (!output->enabled || config_head->state.enabled) { + continue; + } + struct output_config *oc = new_output_config(output->wlr_output->name); + oc->enabled = false; + + oc = store_output_config(oc); + ok &= apply_output_config(oc, output); + } + + // Then enable outputs that need to + wl_list_for_each(config_head, &config->heads, link) { + struct wlr_output *wlr_output = config_head->state.output; + struct sway_output *output = wlr_output->data; + if (!config_head->state.enabled) { + continue; + } + struct output_config *oc = new_output_config(output->wlr_output->name); + oc->enabled = true; + if (config_head->state.mode != NULL) { + struct wlr_output_mode *mode = config_head->state.mode; + oc->width = mode->width; + oc->height = mode->height; + oc->refresh_rate = mode->refresh; + } else { + oc->width = config_head->state.custom_mode.width; + oc->height = config_head->state.custom_mode.height; + oc->refresh_rate = config_head->state.custom_mode.refresh; + } + oc->x = config_head->state.x; + oc->y = config_head->state.y; + oc->transform = config_head->state.transform; + oc->scale = config_head->state.scale; + + oc = store_output_config(oc); + ok &= apply_output_config(oc, output); + } + + if (ok) { + wlr_output_configuration_v1_send_succeeded(config); + } else { + wlr_output_configuration_v1_send_failed(config); + } + wlr_output_configuration_v1_destroy(config); + + update_output_manager_config(server); +} + +void handle_output_manager_test(struct wl_listener *listener, void *data) { + struct wlr_output_configuration_v1 *config = data; + + // TODO: implement test-only mode + wlr_output_configuration_v1_send_succeeded(config); + wlr_output_configuration_v1_destroy(config); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 92e623ef4..6bea56569 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -16,7 +16,6 @@ #include "log.h" #include "config.h" #include "sway/config.h" -#include "sway/debug.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" @@ -157,16 +156,17 @@ static void render_drag_icons(struct sway_output *output, // _box.x and .y are expected to be layout-local // _box.width and .height are expected to be output-buffer-local -void render_rect(struct wlr_output *wlr_output, +void render_rect(struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]) { + struct wlr_output *wlr_output = output->wlr_output; struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= wlr_output->lx * wlr_output->scale; - box.y -= wlr_output->ly * wlr_output->scale; + box.x -= output->lx * wlr_output->scale; + box.y -= output->ly * wlr_output->scale; pixman_region32_t damage; pixman_region32_init(&damage); @@ -205,9 +205,9 @@ static void render_view_toplevels(struct sway_view *view, }; // Render all toplevels without descending into popups double ox = view->container->surface_x - - output->wlr_output->lx - view->geometry.x; + output->lx - view->geometry.x; double oy = view->container->surface_y - - output->wlr_output->ly - view->geometry.y; + output->ly - view->geometry.y; output_surface_for_each_surface(output, view->surface, ox, oy, render_surface_iterator, &data); } @@ -240,9 +240,9 @@ static void render_saved_view(struct sway_view *view, return; } struct wlr_box box = { - .x = view->container->surface_x - output->wlr_output->lx - + .x = view->container->surface_x - output->lx - view->saved_geometry.x, - .y = view->container->surface_y - output->wlr_output->ly - + .y = view->container->surface_y - output->ly - view->saved_geometry.y, .width = view->saved_buffer_width, .height = view->saved_buffer_height, @@ -298,7 +298,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->border_thickness; box.height = state->content_height; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } list_t *siblings = container_get_current_siblings(con); @@ -317,7 +317,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->border_thickness; box.height = state->content_height; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } if (state->border_bottom) { @@ -332,7 +332,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } } @@ -359,8 +359,8 @@ static void render_titlebar(struct sway_output *output, list_t *children = container_get_current_siblings(con); bool is_last_child = children->length == 0 || children->items[children->length - 1] == con; - double output_x = output->wlr_output->lx; - double output_y = output->wlr_output->ly; + double output_x = output->lx; + double output_y = output->ly; int titlebar_border_thickness = config->titlebar_border_thickness; int titlebar_h_padding = config->titlebar_h_padding; int titlebar_v_padding = config->titlebar_v_padding; @@ -374,7 +374,7 @@ static void render_titlebar(struct sway_output *output, box.width = width; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Single pixel bar below title size_t left_offset = 0, right_offset = 0; @@ -392,7 +392,7 @@ static void render_titlebar(struct sway_output *output, box.width = width - left_offset - right_offset; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); if (layout == L_TABBED) { // Single pixel left edge @@ -402,7 +402,7 @@ static void render_titlebar(struct sway_output *output, box.height = container_titlebar_height() - titlebar_border_thickness * 2; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Single pixel right edge box.x = x + width - titlebar_border_thickness; @@ -411,7 +411,7 @@ static void render_titlebar(struct sway_output *output, box.height = container_titlebar_height() - titlebar_border_thickness * 2; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } int inner_x = x - output_x + titlebar_h_padding; @@ -470,12 +470,12 @@ static void render_titlebar(struct sway_output *output, box.y = round((y + titlebar_border_thickness) * output_scale); box.width = texture_box.width; box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding below box.y += ob_padding_above + texture_box.height; box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Title text @@ -538,12 +538,12 @@ static void render_titlebar(struct sway_output *output, box.y = round((y + titlebar_border_thickness) * output_scale); box.width = texture_box.width; box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding below box.y += ob_padding_above + texture_box.height; box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Determine the left + right extends of the textures (output-buffer local) @@ -577,7 +577,7 @@ static void render_titlebar(struct sway_output *output, box.x = ob_left_x + ob_left_width + round(output_x * output_scale); box.y = round(bg_y * output_scale); box.height = ob_bg_height; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Padding on left side @@ -592,7 +592,7 @@ static void render_titlebar(struct sway_output *output, if (box.x + box.width < left_x) { box.width += left_x - box.x - box.width; } - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding on right side right_offset = (layout == L_TABBED) * titlebar_border_thickness; @@ -607,7 +607,7 @@ static void render_titlebar(struct sway_output *output, box.width += box.x - right_rx; box.x = right_rx; } - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); if (connects_sides) { // Left pixel in line with bottom bar @@ -616,7 +616,7 @@ static void render_titlebar(struct sway_output *output, box.width = state->border_thickness * state->border_left; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Right pixel in line with bottom bar box.x = x + width - state->border_thickness * state->border_right; @@ -624,7 +624,7 @@ static void render_titlebar(struct sway_output *output, box.width = state->border_thickness * state->border_right; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } } @@ -650,7 +650,7 @@ static void render_top_border(struct sway_output *output, box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } struct parent_data { @@ -841,6 +841,14 @@ static void render_containers_stacked(struct sway_output *output, static void render_containers(struct sway_output *output, pixman_region32_t *damage, struct parent_data *parent) { + if (config->hide_lone_tab && parent->children->length == 1) { + struct sway_container *child = parent->children->items[0]; + if (child->view) { + render_containers_linear(output,damage, parent); + return; + } + } + switch (parent->layout) { case L_NONE: case L_HORIZ: @@ -936,6 +944,9 @@ static void render_floating(struct sway_output *soutput, } for (int k = 0; k < ws->current.floating->length; ++k) { struct sway_container *floater = ws->current.floating->items[k]; + if (floater->fullscreen_mode != FULLSCREEN_NONE) { + continue; + } render_floating_container(soutput, damage, floater); } } @@ -986,9 +997,6 @@ void output_render(struct sway_output *output, struct timespec *when, } struct sway_container *fullscreen_con = root->fullscreen_global; - if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { - fullscreen_con = NULL; - } if (!fullscreen_con) { fullscreen_con = workspace->current.fullscreen; } @@ -1063,12 +1071,6 @@ render_overlay: render_drag_icons(output, damage, &root->drag_icons); renderer_end: - if (debug.render_tree) { - wlr_renderer_scissor(renderer, NULL); - wlr_render_texture(renderer, root->debug_tree, - wlr_output->transform_matrix, 0, 40, 1); - } - wlr_renderer_scissor(renderer, NULL); wlr_output_render_software_cursors(wlr_output, damage); wlr_renderer_end(renderer); @@ -1076,15 +1078,23 @@ renderer_end: int width, height; wlr_output_transformed_resolution(wlr_output, &width, &height); - if (debug.damage == DAMAGE_HIGHLIGHT) { - pixman_region32_union_rect(damage, damage, 0, 0, width, height); - } + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(damage, damage, transform, width, height); + wlr_region_transform(&frame_damage, &output->damage->current, + transform, width, height); - if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { + if (debug.damage == DAMAGE_HIGHLIGHT) { + pixman_region32_union_rect(&frame_damage, &frame_damage, + 0, 0, wlr_output->width, wlr_output->height); + } + + wlr_output_set_damage(wlr_output, &frame_damage); + pixman_region32_fini(&frame_damage); + + if (!wlr_output_commit(wlr_output)) { return; } output->last_frame = *when; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 4098ed22c..ccf605144 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -5,7 +5,6 @@ #include #include #include "sway/config.h" -#include "sway/debug.h" #include "sway/desktop.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" @@ -350,7 +349,7 @@ static void transaction_progress_queue(void) { list_del(server.transactions, 0); if (!server.transactions->length) { - idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); return; } @@ -428,9 +427,10 @@ static void transaction_commit(struct sway_transaction *transaction) { // means we can send a frame done event to make the client redraw it // as soon as possible. Additionally, this is required if a view is // mapping and its default geometry doesn't intersect an output. - struct timespec when; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); wlr_surface_send_frame_done( - node->sway_container->view->surface, &when); + node->sway_container->view->surface, &now); } if (node_is_view(node) && !node->sway_container->view->saved_buffer) { view_save_buffer(node->sway_container->view); @@ -465,11 +465,6 @@ static void transaction_commit(struct sway_transaction *transaction) { transaction->num_waiting = 0; } } - - // The debug tree shows the pending/live tree. Here is a good place to - // update it, because we make a transaction every time we change the pending - // tree. - update_debug_tree(); } static void set_instruction_ready( diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ce6fe41a1..9e914f144 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -17,6 +17,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#include "sway/xdg_decoration.h" static const struct sway_view_child_impl popup_impl; @@ -338,6 +339,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } + if (e->fullscreen && e->output && e->output->data) { + struct sway_output *output = e->output->data; + struct sway_workspace *ws = output_get_active_workspace(output); + if (ws && !container_is_scratchpad_hidden(view->container)) { + if (container_is_floating(view->container)) { + workspace_add_floating(ws, view->container); + } else { + workspace_add_tiling(ws, view->container); + } + } + } + container_set_fullscreen(view->container, e->fullscreen); arrange_root(); @@ -354,7 +367,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } } @@ -368,8 +381,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } } @@ -417,7 +429,9 @@ static void handle_map(struct wl_listener *listener, void *data) { } view_map(view, view->wlr_xdg_surface->surface, - xdg_surface->toplevel->client_pending.fullscreen, csd); + xdg_surface->toplevel->client_pending.fullscreen, + xdg_surface->toplevel->client_pending.fullscreen_output, + csd); transaction_commit_dirty(); @@ -461,6 +475,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); view->wlr_xdg_surface = NULL; + if (view->xdg_decoration) { + view->xdg_decoration->view = NULL; + } view_begin_destroy(view); } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 3eed54ab0..5ac589cfd 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -339,6 +339,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } + if (e->fullscreen && e->output && e->output->data) { + struct sway_output *output = e->output->data; + struct sway_workspace *ws = output_get_active_workspace(output); + if (ws && !container_is_scratchpad_hidden(view->container)) { + if (container_is_floating(view->container)) { + workspace_add_floating(ws, view->container); + } else { + workspace_add_tiling(ws, view->container); + } + } + } + container_set_fullscreen(view->container, e->fullscreen); arrange_root(); @@ -355,7 +367,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_v6_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } } @@ -369,8 +381,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } } @@ -412,7 +423,9 @@ static void handle_map(struct wl_listener *listener, void *data) { == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; view_map(view, view->wlr_xdg_surface_v6->surface, - xdg_surface->toplevel->client_pending.fullscreen, csd); + xdg_surface->toplevel->client_pending.fullscreen, + xdg_surface->toplevel->client_pending.fullscreen_output, + csd); transaction_commit_dirty(); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e0d307e86..f6ca8f818 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -415,7 +415,7 @@ static void handle_map(struct wl_listener *listener, void *data) { xwayland_view->commit.notify = handle_commit; // Put it back into the tree - view_map(view, xsurface->surface, xsurface->fullscreen, false); + view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); transaction_commit_dirty(); } @@ -432,8 +432,16 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { return; } if (container_is_floating(view->container)) { - configure(view, view->container->current.content_x, - view->container->current.content_y, ev->width, ev->height); + // Respect minimum and maximum sizes + view->natural_width = ev->width; + view->natural_height = ev->height; + container_floating_resize_and_center(view->container); + + configure(view, view->container->content_x, + view->container->content_y, + view->container->content_width, + view->container->content_height); + node_set_dirty(&view->container->node); } else { configure(view, view->container->current.content_x, view->container->current.content_y, @@ -468,7 +476,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { return; } struct sway_seat *seat = input_manager_current_seat(); - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -484,8 +492,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { } struct wlr_xwayland_resize_event *e = data; struct sway_seat *seat = input_manager_current_seat(); - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } static void handle_request_activate(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fb1edd0b0..610844479 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -158,132 +158,9 @@ struct sway_node *node_at_coords( return &ws->node; } -/** - * Determine if the edge of the given container is on the edge of the - * workspace/output. - */ -static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { - enum sway_container_layout layout = L_NONE; - switch (edge) { - case WLR_EDGE_TOP: - case WLR_EDGE_BOTTOM: - layout = L_VERT; - break; - case WLR_EDGE_LEFT: - case WLR_EDGE_RIGHT: - layout = L_HORIZ; - break; - case WLR_EDGE_NONE: - sway_assert(false, "Never reached"); - return false; - } - - // Iterate the parents until we find one with the layout we want, - // then check if the child has siblings between it and the edge. - while (cont) { - if (container_parent_layout(cont) == layout) { - list_t *siblings = container_get_siblings(cont); - int index = list_find(siblings, cont); - if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { - return false; - } - if (index < siblings->length - 1 && - (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { - return false; - } - } - cont = cont->parent; - } - return true; -} - -static enum wlr_edges find_edge(struct sway_container *cont, - struct sway_cursor *cursor) { - if (!cont->view) { - return WLR_EDGE_NONE; - } - if (cont->border == B_NONE || !cont->border_thickness || - cont->border == B_CSD) { - return WLR_EDGE_NONE; - } - - enum wlr_edges edge = 0; - if (cursor->cursor->x < cont->x + cont->border_thickness) { - edge |= WLR_EDGE_LEFT; - } - if (cursor->cursor->y < cont->y + cont->border_thickness) { - edge |= WLR_EDGE_TOP; - } - if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { - edge |= WLR_EDGE_RIGHT; - } - if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { - edge |= WLR_EDGE_BOTTOM; - } - - return edge; -} - -/** - * If the cursor is over a _resizable_ edge, return the edge. - * Edges that can't be resized are edges of the workspace. - */ -static enum wlr_edges find_resize_edge(struct sway_container *cont, - struct sway_cursor *cursor) { - enum wlr_edges edge = find_edge(cont, cursor); - if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { - return WLR_EDGE_NONE; - } - return edge; -} - -static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, - double sx, double sy) { - // Handle cursor image - if (surface) { - // Reset cursor if switching between clients - struct wl_client *client = wl_resource_get_client(surface->resource); - if (client != cursor->image_client) { - cursor_set_image(cursor, "left_ptr", client); - } - } else if (node && node->type == N_CONTAINER) { - // Try a node's resize edge - enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); - if (edge == WLR_EDGE_NONE) { - cursor_set_image(cursor, "left_ptr", NULL); - } else if (container_is_floating(node->sway_container)) { - cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); - } else { - if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { - cursor_set_image(cursor, "col-resize", NULL); - } else { - cursor_set_image(cursor, "row-resize", NULL); - } - } - } else { - cursor_set_image(cursor, "left_ptr", NULL); - } - - // Send pointer enter/leave - struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; - if (surface) { - if (seat_is_input_allowed(cursor->seat, surface)) { - wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy); - } - } else { - wlr_seat_pointer_clear_focus(wlr_seat); - } -} - void cursor_rebase(struct sway_cursor *cursor) { uint32_t time_msec = get_current_time_msec(); - struct wlr_surface *surface = NULL; - double sx, sy; - cursor->previous.node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); + seatop_rebase(cursor->seat, time_msec); } void cursor_rebase_all(void) { @@ -293,9 +170,7 @@ void cursor_rebase_all(void) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - if (!seat_doing_seatop(seat)) { - cursor_rebase(seat->cursor); - } + cursor_rebase(seat->cursor); } } @@ -345,93 +220,20 @@ void cursor_unhide(struct sway_cursor *cursor) { cursor_rebase(cursor); } -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, - double sx, double sy) { - if (time_msec == 0) { - time_msec = get_current_time_msec(); - } - - struct sway_seat *seat = cursor->seat; - struct wlr_seat *wlr_seat = seat->wlr_seat; - - if (seat_doing_seatop(seat)) { - seatop_motion(seat, time_msec); - cursor->previous.x = cursor->cursor->x; - cursor->previous.y = cursor->cursor->y; - return; - } - - struct sway_node *prev_node = cursor->previous.node; - - // Update the stored previous position - cursor->previous.x = cursor->cursor->x; - cursor->previous.y = cursor->cursor->y; - cursor->previous.node = node; - - if (node && (config->focus_follows_mouse == FOLLOWS_YES || - config->focus_follows_mouse == FOLLOWS_ALWAYS)) { - struct sway_node *focus = seat_get_focus(seat); - if (focus && node->type == N_WORKSPACE) { - // Only follow the mouse if it would move to a new output - // Otherwise we'll focus the workspace, which is probably wrong - struct sway_output *focused_output = node_get_output(focus); - struct sway_output *output = node_get_output(node); - if (output != focused_output) { - seat_set_focus(seat, seat_get_focus_inactive(seat, node)); - } - } else if (node->type == N_CONTAINER && node->sway_container->view) { - // Focus node if the following are true: - // - cursor is over a new view, i.e. entered a new window; and - // - the new view is visible, i.e. not hidden in a stack or tab; and - // - the seat does not have a keyboard grab - if ((!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && - node != prev_node && - view_is_visible(node->sway_container->view)) || - config->focus_follows_mouse == FOLLOWS_ALWAYS) { - seat_set_focus(seat, node); - } else { - struct sway_node *next_focus = - seat_get_focus_inactive(seat, &root->node); - if (next_focus && next_focus->type == N_CONTAINER && - next_focus->sway_container->view && - view_is_visible(next_focus->sway_container->view)) { - seat_set_focus(seat, next_focus); - } - } - } - } - - cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); - - struct wlr_drag_icon *wlr_drag_icon; - wl_list_for_each(wlr_drag_icon, &wlr_seat->drag_icons, link) { - struct sway_drag_icon *drag_icon = wlr_drag_icon->data; - drag_icon_update_position(drag_icon); - } -} - -static void handle_cursor_motion(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - struct wlr_event_pointer_motion *event = data; +static void cursor_motion(struct sway_cursor *cursor, uint32_t time_msec, + struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) { cursor_handle_activity(cursor); - double dx = event->delta_x; - double dy = event->delta_y; - - double dx_unaccel = event->unaccel_dx; - double dy_unaccel = event->unaccel_dy; - wlr_relative_pointer_manager_v1_send_relative_motion( server.relative_pointer_manager, - cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, + cursor->seat->wlr_seat, (uint64_t)time_msec * 1000, dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; - struct sway_node *node = NULL; double sx, sy; if (cursor->active_constraint) { - node = node_at_coords(cursor->seat, + node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (cursor->active_constraint->surface != surface) { @@ -448,50 +250,18 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { dy = sy_confined - sy; } - wlr_cursor_move(cursor->cursor, event->device, dx, dy); + wlr_cursor_move(cursor->cursor, device, dx, dy); - // Recalculate pointer location after layout checks - node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - - cursor_send_pointer_motion(cursor, event->time_msec, node, surface, - sx, sy); - transaction_commit_dirty(); + seatop_motion(cursor->seat, time_msec, dx, dy); } -static void cursor_motion_absolute(struct sway_cursor *cursor, - uint32_t time_msec, struct wlr_input_device *dev, - double x, double y) { - cursor_handle_activity(cursor); +static void handle_cursor_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; - double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, dev, - x, y, &lx, &ly); - - double dx = lx - cursor->cursor->x; - double dy = ly - cursor->cursor->y; - wlr_relative_pointer_manager_v1_send_relative_motion( - server.relative_pointer_manager, - cursor->seat->wlr_seat, (uint64_t)time_msec * 1000, - dx, dy, dx, dy); - - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(cursor->seat, - lx, ly, &surface, &sx, &sy); - - if (cursor->active_constraint) { - if (cursor->active_constraint->surface != surface) { - return; - } - if (!pixman_region32_contains_point(&cursor->confine, - floor(sx), floor(sy), NULL)) { - return; - } - } - - wlr_cursor_warp_closest(cursor->cursor, dev, lx, ly); - cursor_send_pointer_motion(cursor, time_msec, node, surface, sx, sy); + cursor_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, + e->unaccel_dx, e->unaccel_dy); transaction_commit_dirty(); } @@ -501,98 +271,15 @@ static void handle_cursor_motion_absolute( wl_container_of(listener, cursor, motion_absolute); struct wlr_event_pointer_motion_absolute *event = data; - cursor_motion_absolute(cursor, event->time_msec, event->device, - event->x, event->y); -} + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + event->x, event->y, &lx, &ly); -/** - * Remove a button (and duplicates) to the sorted list of currently pressed buttons - */ -static void state_erase_button(struct sway_cursor *cursor, uint32_t button) { - size_t j = 0; - for (size_t i = 0; i < cursor->pressed_button_count; ++i) { - if (i > j) { - cursor->pressed_buttons[j] = cursor->pressed_buttons[i]; - } - if (cursor->pressed_buttons[i] != button) { - ++j; - } - } - while (cursor->pressed_button_count > j) { - --cursor->pressed_button_count; - cursor->pressed_buttons[cursor->pressed_button_count] = 0; - } -} + double dx = lx - cursor->cursor->x; + double dy = ly - cursor->cursor->y; -/** - * Add a button to the sorted list of currently pressed buttons, if there - * is space. - */ -static void state_add_button(struct sway_cursor *cursor, uint32_t button) { - if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { - return; - } - size_t i = 0; - while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) { - ++i; - } - size_t j = cursor->pressed_button_count; - while (j > i) { - cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1]; - --j; - } - cursor->pressed_buttons[i] = button; - cursor->pressed_button_count++; -} - -/** - * Return the mouse binding which matches modifier, click location, release, - * and pressed button state, otherwise return null. - */ -static struct sway_binding* get_active_mouse_binding( - const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers, - bool release, bool on_titlebar, bool on_border, bool on_content, - bool on_workspace, const char *identifier) { - uint32_t click_region = - ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | - ((on_border || on_workspace) ? BINDING_BORDER : 0) | - ((on_content || on_workspace) ? BINDING_CONTENTS : 0); - - struct sway_binding *current = NULL; - for (int i = 0; i < bindings->length; ++i) { - struct sway_binding *binding = bindings->items[i]; - if (modifiers ^ binding->modifiers || - cursor->pressed_button_count != (size_t)binding->keys->length || - release != (binding->flags & BINDING_RELEASE) || - !(click_region & binding->flags) || - (on_workspace && - (click_region & binding->flags) != click_region) || - (strcmp(binding->input, identifier) != 0 && - strcmp(binding->input, "*") != 0)) { - continue; - } - - bool match = true; - for (size_t j = 0; j < cursor->pressed_button_count; j++) { - uint32_t key = *(uint32_t *)binding->keys->items[j]; - if (key != cursor->pressed_buttons[j]) { - match = false; - break; - } - } - if (!match) { - continue; - } - - if (!current || strcmp(current->input, "*") == 0) { - current = binding; - if (strcmp(current->input, identifier) == 0) { - // If a binding is found for the exact input, quit searching - break; - } - } - } - return current; + cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); + transaction_commit_dirty(); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -601,320 +288,33 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (time_msec == 0) { time_msec = get_current_time_msec(); } - struct sway_seat *seat = cursor->seat; - // Handle existing seat operation - if (seat_doing_seatop(seat)) { - if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { - seatop_finish(seat); - seat_pointer_notify_button(seat, time_msec, button, state); - } - if (state == WLR_BUTTON_PRESSED) { - state_add_button(cursor, button); - } else { - state_erase_button(cursor, button); - } - return; - } - - // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - struct sway_container *cont = node && node->type == N_CONTAINER ? - node->sway_container : NULL; - bool is_floating = cont && container_is_floating(cont); - bool is_floating_or_child = cont && container_is_floating_or_child(cont); - bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); - enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; - enum wlr_edges resize_edge = edge ? - find_resize_edge(cont, cursor) : WLR_EDGE_NONE; - bool on_border = edge != WLR_EDGE_NONE; - bool on_contents = cont && !on_border && surface; - bool on_workspace = node && node->type == N_WORKSPACE; - bool on_titlebar = cont && !on_border && !surface; - - // Handle mouse bindings - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - - char *device_identifier = device ? input_device_get_identifier(device) - : strdup("*"); - struct sway_binding *binding = NULL; - if (state == WLR_BUTTON_PRESSED) { - state_add_button(cursor, button); - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, on_workspace, - device_identifier); - } else { - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, on_workspace, - device_identifier); - state_erase_button(cursor, button); - } - free(device_identifier); - if (binding) { - seat_execute_command(seat, binding); - return; - } - - // Handle clicking an empty workspace - if (node && node->type == N_WORKSPACE) { - seat_set_focus(seat, node); - return; - } - - // Handle clicking a layer surface - if (surface && wlr_surface_is_layer_surface(surface)) { - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); - if (layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, layer); - } - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - // Handle tiling resize via border - if (cont && resize_edge && button == BTN_LEFT && - state == WLR_BUTTON_PRESSED && !is_floating) { - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, button, edge); - return; - } - - // Handle tiling resize via mod - bool mod_pressed = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - if (cont && !is_floating_or_child && mod_pressed && - state == WLR_BUTTON_PRESSED) { - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (button == btn_resize) { - edge = 0; - edge |= cursor->cursor->x > cont->x + cont->width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->y + cont->height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - - const char *image = NULL; - if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { - image = "nw-resize"; - } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { - image = "ne-resize"; - } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { - image = "se-resize"; - } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { - image = "sw-resize"; - } - cursor_set_image(seat->cursor, image, NULL); - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, button, edge); - return; - } - } - - // 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 && state == WLR_BUTTON_PRESSED && - (mod_pressed || on_titlebar)) { - while (cont->parent) { - cont = cont->parent; - } - seat_set_focus_container(seat, cont); - seatop_begin_move_floating(seat, cont, button); - return; - } - } - - // Handle beginning floating resize - if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WLR_BUTTON_PRESSED) { - // Via border - if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { - seatop_begin_resize_floating(seat, cont, button, resize_edge); - return; - } - - // Via mod+click - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (mod_pressed && button == btn_resize) { - struct sway_container *floater = cont; - while (floater->parent) { - floater = floater->parent; - } - edge = 0; - edge |= cursor->cursor->x > floater->x + floater->width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > floater->y + floater->height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - seatop_begin_resize_floating(seat, floater, button, edge); - return; - } - } - - // Handle moving a tiling container - if (config->tiling_drag && (mod_pressed || on_titlebar) && - state == WLR_BUTTON_PRESSED && !is_floating_or_child && - cont && cont->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); - } - - seat_pointer_notify_button(seat, time_msec, button, state); - - // If moving a container by it's title bar, use a threshold for the drag - if (!mod_pressed && config->tiling_drag_threshold > 0) { - seatop_begin_move_tiling_threshold(seat, cont, button); - } else { - seatop_begin_move_tiling(seat, cont, button); - } - return; - } - - // Handle mousedown on a container surface - if (surface && cont && state == WLR_BUTTON_PRESSED) { - seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); - seatop_begin_down(seat, cont, button, sx, sy); - return; - } - - // Handle clicking a container surface or decorations - if (cont) { - node = seat_get_focus_inactive(seat, &cont->node); - seat_set_focus(seat, node); - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - seat_pointer_notify_button(seat, time_msec, button, state); + seatop_button(cursor->seat, time_msec, device, button, state); } static void handle_cursor_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, button); struct wlr_event_pointer_button *event = data; cursor_handle_activity(cursor); + + if (event->state == WLR_BUTTON_PRESSED) { + cursor->pressed_button_count++; + } else { + if (cursor->pressed_button_count > 0) { + cursor->pressed_button_count--; + } else { + sway_log(SWAY_ERROR, "Pressed button count was wrong"); + } + } + dispatch_cursor_button(cursor, event->device, event->time_msec, event->button, event->state); transaction_commit_dirty(); } -static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { - switch (event->orientation) { - case WLR_AXIS_ORIENTATION_VERTICAL: - return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; - case WLR_AXIS_ORIENTATION_HORIZONTAL: - return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; - default: - sway_log(SWAY_DEBUG, "Unknown axis orientation"); - return 0; - } -} - void dispatch_cursor_axis(struct sway_cursor *cursor, struct wlr_event_pointer_axis *event) { - struct sway_seat *seat = cursor->seat; - struct sway_input_device *input_device = - event->device ? event->device->data : NULL; - struct input_config *ic = - input_device ? input_device_get_config(input_device) : NULL; - - // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - struct sway_container *cont = node && node->type == N_CONTAINER ? - node->sway_container : NULL; - enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; - bool on_border = edge != WLR_EDGE_NONE; - bool on_titlebar = cont && !on_border && !surface; - bool on_titlebar_border = cont && on_border && - cursor->cursor->y < cont->content_y; - bool on_contents = cont && !on_border && surface; - bool on_workspace = node && node->type == N_WORKSPACE; - float scroll_factor = - (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; - - bool handled = false; - - // Gather information needed for mouse bindings - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - struct wlr_input_device *device = - input_device ? input_device->wlr_device : NULL; - char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); - uint32_t button = wl_axis_to_button(event); - - // Handle mouse bindings - x11 mouse buttons 4-7 - press event - struct sway_binding *binding = NULL; - state_add_button(cursor, button); - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, on_workspace, dev_id); - if (binding) { - seat_execute_command(seat, binding); - handled = true; - } - - // Scrolling on a tabbed or stacked title bar (handled as press event) - if (!handled && (on_titlebar || on_titlebar_border)) { - enum sway_container_layout layout = container_parent_layout(cont); - if (layout == L_TABBED || layout == L_STACKED) { - struct sway_node *tabcontainer = node_get_parent(node); - struct sway_node *active = - 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); - if (desired < 0) { - desired = 0; - } else if (desired >= siblings->length) { - desired = siblings->length - 1; - } - struct sway_node *old_focus = seat_get_focus(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); - if (node_has_ancestor(old_focus, tabcontainer)) { - seat_set_focus(seat, new_focus); - } else { - // Scrolling when focus is not in the tabbed container at all - seat_set_raw_focus(seat, new_sibling); - seat_set_raw_focus(seat, new_focus); - seat_set_raw_focus(seat, old_focus); - } - handled = true; - } - } - - // Handle mouse bindings - x11 mouse buttons 4-7 - release event - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, on_workspace, dev_id); - state_erase_button(cursor, button); - if (binding) { - seat_execute_command(seat, binding); - handled = true; - } - free(dev_id); - - if (!handled) { - wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, - round(scroll_factor * event->delta_discrete), event->source); - } + seatop_axis(cursor->seat, event); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { @@ -991,10 +391,11 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { seat->touch_x = lx; seat->touch_y = ly; - struct wlr_drag_icon *wlr_drag_icon; - wl_list_for_each(wlr_drag_icon, &wlr_seat->drag_icons, link) { - struct sway_drag_icon *drag_icon = wlr_drag_icon->data; - drag_icon_update_position(drag_icon); + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } } } @@ -1055,8 +456,16 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); } - cursor_motion_absolute(cursor, event->time_msec, event->device, x, y); + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + x, y, &lx, &ly); + + double dx = lx - cursor->cursor->x; + double dy = ly - cursor->cursor->y; + + cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + transaction_commit_dirty(); } static void handle_tool_tip(struct wl_listener *listener, void *data) { @@ -1143,7 +552,7 @@ static void handle_request_set_cursor(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, request_set_cursor); - if (seat_doing_seatop(cursor->seat)) { + if (!seatop_allows_set_cursor(cursor->seat)) { return; } struct wlr_seat_pointer_request_set_cursor_event *event = data; @@ -1258,7 +667,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); - cursor->motion.notify = handle_cursor_motion; + cursor->motion.notify = handle_cursor_motion_relative; wl_signal_add(&wlr_cursor->events.motion_absolute, &cursor->motion_absolute); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f99fc395b..a2a1e2740 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -71,6 +71,71 @@ char *input_device_get_identifier(struct wlr_input_device *device) { return identifier; } +static bool device_is_touchpad(struct sway_input_device *device) { + if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || + !wlr_input_device_is_libinput(device->wlr_device)) { + return false; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device->wlr_device); + + return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +} + +const char *input_device_get_type(struct sway_input_device *device) { + switch (device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + if (device_is_touchpad(device)) { + return "touchpad"; + } else { + return "pointer"; + } + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; + } + return "unknown"; +} + +static void apply_input_type_config(struct sway_input_device *input_device) { + const char *device_type = input_device_get_type(input_device); + struct input_config *type_config = NULL; + for (int i = 0; i < config->input_type_configs->length; i++) { + struct input_config *ic = config->input_type_configs->items[i]; + if (strcmp(ic->identifier + 5, device_type) == 0) { + type_config = ic; + break; + } + } + if (type_config == NULL) { + return; + } + + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (strcmp(input_device->identifier, ic->identifier) == 0) { + struct input_config *current = new_input_config(ic->identifier); + merge_input_config(current, type_config); + merge_input_config(current, ic); + + current->input_type = device_type; + config->input_configs->items[i] = current; + free_input_config(ic); + ic = NULL; + + break; + } + } +} + static struct sway_input_device *input_sway_device_from_wlr( struct wlr_input_device *device) { struct sway_input_device *input_device = NULL; @@ -155,6 +220,47 @@ static void input_manager_libinput_reset_keyboard( libinput_device, send_events)); } +static void input_manager_libinput_config_switch( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct input_config *ic = input_device_get_config(input_device); + struct libinput_device *libinput_device; + + if (!ic || !wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + sway_log(SWAY_DEBUG, "input_manager_libinput_config_switch(%s)", + ic->identifier); + + if (ic->send_events != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_switch(%s) send_events_set_mode(%d)", + ic->identifier, ic->send_events); + log_libinput_config_status(libinput_device_config_send_events_set_mode( + libinput_device, ic->send_events)); + } +} + +static void input_manager_libinput_reset_switch( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct libinput_device *libinput_device; + + if (!wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + + uint32_t send_events = + libinput_device_config_send_events_get_default_mode(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_switch(%s) send_events_set_mode(%d)", + input_device->identifier, send_events); + log_libinput_config_status(libinput_device_config_send_events_set_mode( + libinput_device, send_events)); +} + static void input_manager_libinput_config_touch( struct sway_input_device *input_device) { struct wlr_input_device *wlr_device = input_device->wlr_device; @@ -466,11 +572,15 @@ static void handle_new_input(struct wl_listener *listener, void *data) { sway_log(SWAY_DEBUG, "adding device: '%s'", input_device->identifier); + apply_input_type_config(input_device); + if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_config_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_config_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_config_touch(input_device); } @@ -616,14 +726,20 @@ void input_manager_set_focus(struct sway_node *node) { void input_manager_apply_input_config(struct input_config *input_config) { struct sway_input_device *input_device = NULL; bool wildcard = strcmp(input_config->identifier, "*") == 0; + bool type_wildcard = strncmp(input_config->identifier, "type:", 5) == 0; wl_list_for_each(input_device, &server.input->devices, link) { + bool type_matches = type_wildcard && + strcmp(input_device_get_type(input_device), input_config->identifier + 5) == 0; if (strcmp(input_device->identifier, input_config->identifier) == 0 - || wildcard) { + || wildcard + || type_matches) { if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_config_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_config_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_config_touch(input_device); } @@ -642,6 +758,8 @@ void input_manager_reset_input(struct sway_input_device *input_device) { input_manager_libinput_reset_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_reset_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_reset_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_reset_touch(input_device); } @@ -748,5 +866,13 @@ struct input_config *input_device_get_config(struct sway_input_device *device) { } } + const char *device_type = input_device_get_type(device); + for (int i = 0; i < config->input_type_configs->length; ++i) { + input_config = config->input_type_configs->items[i]; + if (strcmp(input_config->identifier + 5, device_type) == 0) { + return input_config; + } + } + return wildcard_config; } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 00fc6a13f..dcfaa4fad 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -146,7 +146,7 @@ static void get_active_binding(const struct sway_shortcut_state *state, uint32_t modifiers, bool release, bool locked, const char *input) { for (int i = 0; i < bindings->length; ++i) { struct sway_binding *binding = bindings->items[i]; - bool binding_locked = binding->flags & BINDING_LOCKED; + bool binding_locked = (binding->flags & BINDING_LOCKED) != 0; bool binding_release = binding->flags & BINDING_RELEASE; if (modifiers ^ binding->modifiers || @@ -178,18 +178,37 @@ static void get_active_binding(const struct sway_shortcut_state *state, continue; } - if (*current_binding && *current_binding != binding && - strcmp((*current_binding)->input, binding->input) == 0) { - sway_log(SWAY_DEBUG, "encountered duplicate bindings %d and %d", - (*current_binding)->order, binding->order); - } else if (!*current_binding || - strcmp((*current_binding)->input, "*") == 0) { - *current_binding = binding; - - if (strcmp((*current_binding)->input, input) == 0) { - // If a binding is found for the exact input, quit searching - return; + if (*current_binding) { + if (*current_binding == binding) { + continue; } + + bool current_locked = + ((*current_binding)->flags & BINDING_LOCKED) != 0; + bool current_input = strcmp((*current_binding)->input, input) == 0; + bool binding_input = strcmp(binding->input, input) == 0; + + if (current_input == binding_input + && current_locked == binding_locked) { + sway_log(SWAY_DEBUG, + "Encountered conflicting bindings %d and %d", + (*current_binding)->order, binding->order); + continue; + } + + if (current_input && !binding_input) { + continue; // Prefer the correct input + } + + if (current_input == binding_input && current_locked == locked) { + continue; // Prefer correct lock state for matching inputs + } + } + + *current_binding = binding; + if (strcmp((*current_binding)->input, input) == 0 && + (((*current_binding)->flags & BINDING_LOCKED) == locked)) { + return; // If a perfect match is found, quit searching } } } @@ -292,32 +311,32 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { // Identify new keycode, raw keysym(s), and translated keysym(s) xkb_keycode_t keycode = event->keycode + 8; + const xkb_keysym_t *raw_keysyms; + uint32_t raw_modifiers; + size_t raw_keysyms_len = + keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); + const xkb_keysym_t *translated_keysyms; uint32_t translated_modifiers; size_t translated_keysyms_len = keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, &translated_modifiers); - const xkb_keysym_t *raw_keysyms; - uint32_t raw_modifiers; - size_t raw_keysyms_len = - keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); - uint32_t code_modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); // Update shortcut model state update_shortcut_state(&keyboard->state_keycodes, event, (uint32_t)keycode, code_modifiers); - for (size_t i = 0; i < translated_keysyms_len; ++i) { - update_shortcut_state(&keyboard->state_keysyms_translated, - event, (uint32_t)translated_keysyms[i], - code_modifiers); - } for (size_t i = 0; i < raw_keysyms_len; ++i) { update_shortcut_state(&keyboard->state_keysyms_raw, event, (uint32_t)raw_keysyms[i], code_modifiers); } + for (size_t i = 0; i < translated_keysyms_len; ++i) { + update_shortcut_state(&keyboard->state_keysyms_translated, + event, (uint32_t)translated_keysyms[i], + code_modifiers); + } bool handled = false; @@ -326,12 +345,12 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding_released, code_modifiers, true, input_inhibited, device_identifier); - get_active_binding(&keyboard->state_keysyms_translated, - config->current_mode->keysym_bindings, &binding_released, - translated_modifiers, true, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding_released, raw_modifiers, true, input_inhibited, device_identifier); + get_active_binding(&keyboard->state_keysyms_translated, + config->current_mode->keysym_bindings, &binding_released, + translated_modifiers, true, input_inhibited, device_identifier); // Execute stored release binding once no longer active if (keyboard->held_binding && binding_released != keyboard->held_binding && @@ -352,13 +371,13 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding, code_modifiers, false, input_inhibited, device_identifier); + get_active_binding(&keyboard->state_keysyms_raw, + config->current_mode->keysym_bindings, &binding, + raw_modifiers, false, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding, translated_modifiers, false, input_inhibited, device_identifier); - get_active_binding(&keyboard->state_keysyms_raw, - config->current_mode->keysym_bindings, &binding, - raw_modifiers, false, input_inhibited, device_identifier); } // Set up (or clear) keyboard repeat for a pressed binding. Since the @@ -424,11 +443,18 @@ static int handle_keyboard_repeat(void *data) { static void determine_bar_visibility(uint32_t modifiers) { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; - if (strcmp(bar->mode, bar->hidden_state) == 0) { // both are "hide" - bool should_be_visible = - bar->modifier != 0 && (~modifiers & bar->modifier) == 0; - if (bar->visible_by_modifier != should_be_visible) { - bar->visible_by_modifier = should_be_visible; + if (bar->modifier == 0) { + continue; + } + + bool vis_by_mod = (~modifiers & bar->modifier) == 0; + if (bar->visible_by_modifier != vis_by_mod) { + // If visible by modifier is set, send that it is no longer visible + // by modifier (regardless of bar mode and state). Otherwise, only + // send the visible by modifier status if mode and state are hide + if (bar->visible_by_modifier + || strcmp(bar->mode, bar->hidden_state) == 0) { + bar->visible_by_modifier = vis_by_mod; ipc_event_bar_state_update(bar); } } @@ -469,55 +495,38 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, return keyboard; } +struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic) { + struct xkb_rule_names rules = {0}; + if (ic) { + input_config_fill_rule_names(ic, &rules); + } + + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!sway_assert(context, "cannot create XKB context")) { + return NULL; + } + + struct xkb_keymap *keymap = + xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + xkb_context_unref(context); + return keymap; +} + void sway_keyboard_configure(struct sway_keyboard *keyboard) { - struct xkb_rule_names rules; - memset(&rules, 0, sizeof(rules)); 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 (input_config && input_config->xkb_layout) { - rules.layout = input_config->xkb_layout; - } else { - rules.layout = getenv("XKB_DEFAULT_LAYOUT"); - } - if (input_config && input_config->xkb_model) { - rules.model = input_config->xkb_model; - } else { - rules.model = getenv("XKB_DEFAULT_MODEL"); - } - - if (input_config && input_config->xkb_options) { - rules.options = input_config->xkb_options; - } else { - rules.options = getenv("XKB_DEFAULT_OPTIONS"); - } - - if (input_config && input_config->xkb_rules) { - rules.rules = input_config->xkb_rules; - } else { - rules.rules = getenv("XKB_DEFAULT_RULES"); - } - - if (input_config && input_config->xkb_variant) { - rules.variant = input_config->xkb_variant; - } else { - rules.variant = getenv("XKB_DEFAULT_VARIANT"); - } - - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (!sway_assert(context, "cannot create XKB context")) { - return; - } - - struct xkb_keymap *keymap = - xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - + struct xkb_keymap *keymap = sway_keyboard_compile_keymap(input_config); if (!keymap) { - sway_log(SWAY_DEBUG, "cannot configure keyboard: keymap does not exist"); - xkb_context_unref(context); - return; + sway_log(SWAY_ERROR, "Failed to compile keymap. Attempting defaults"); + keymap = sway_keyboard_compile_keymap(NULL); + if (!keymap) { + sway_log(SWAY_ERROR, + "Failed to compile default keymap. Aborting configure"); + return; + } } xkb_keymap_unref(keyboard->keymap); @@ -560,7 +569,6 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wlr_keyboard_set_repeat_info(wlr_device->keyboard, repeat_rate, repeat_delay); - xkb_context_unref(context); struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; wlr_seat_set_keyboard(seat, wlr_device); diff --git a/sway/input/seat.c b/sway/input/seat.c index 69b048430..ce009d7e4 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -10,12 +10,12 @@ #include #include "config.h" #include "log.h" -#include "sway/debug.h" #include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" +#include "sway/input/switch.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -44,7 +44,8 @@ void seat_destroy(struct sway_seat *seat) { } sway_cursor_destroy(seat->cursor); wl_list_remove(&seat->new_node.link); - wl_list_remove(&seat->new_drag_icon.link); + wl_list_remove(&seat->request_start_drag.link); + wl_list_remove(&seat->start_drag.link); wl_list_remove(&seat->request_set_selection.link); wl_list_remove(&seat->request_set_primary_selection.link); wl_list_remove(&seat->link); @@ -146,6 +147,19 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { struct sway_node *focus = seat_get_focus(seat); if (node->type == N_WORKSPACE) { + // 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 + // value of seat->workspace. + if (seat->workspace == node->sway_workspace) { + struct sway_node *node = seat_get_focus_inactive(seat, &root->node); + seat_set_focus(seat, NULL); + if (node) { + seat_set_focus(seat, node); + } else { + seat->workspace = NULL; + } + } seat_node_destroy(seat_node); return; } @@ -158,14 +172,14 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { seat_node_destroy(seat_node); - if (!parent) { + if (!parent && !needs_new_focus) { // Destroying a container that is no longer in the tree return; } // Find new focus_inactive (ie. sibling, or workspace if no siblings left) struct sway_node *next_focus = NULL; - while (next_focus == NULL) { + while (next_focus == NULL && parent != NULL) { struct sway_container *con = seat_get_focus_inactive_view(seat, parent); next_focus = con ? &con->node : NULL; @@ -178,6 +192,16 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { parent = node_get_parent(parent); } + if (!next_focus) { + struct sway_workspace *ws = seat_get_last_known_workspace(seat); + if (!ws) { + return; + } + struct sway_container *con = + seat_get_focus_inactive_view(seat, &ws->node); + next_focus = con ? &(con->node) : &(ws->node); + } + if (next_focus->type == N_WORKSPACE && !workspace_is_visible(next_focus->sway_workspace)) { // Do not change focus to a non-visible workspace @@ -185,6 +209,10 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { } if (needs_new_focus) { + // Make sure the workspace IPC event gets sent + if (node->type == N_CONTAINER && node->sway_container->scratchpad) { + seat_set_focus(seat, NULL); + } // The structure change might have caused it to move up to the top of // the focus stack without sending focus notifications to the view seat_send_focus(next_focus, seat); @@ -193,7 +221,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { // Setting focus_inactive focus = seat_get_focus_inactive(seat, &root->node); seat_set_raw_focus(seat, next_focus); - if (focus->type == N_CONTAINER) { + if (focus->type == N_CONTAINER && focus->sway_container->workspace) { seat_set_raw_focus(seat, &focus->sway_container->workspace->node); } seat_set_raw_focus(seat, focus); @@ -248,12 +276,16 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; struct sway_seat *seat = icon->seat; struct wlr_cursor *cursor = seat->cursor->cursor; - if (wlr_icon->is_pointer) { + switch (wlr_icon->drag->grab_type) { + case WLR_DRAG_GRAB_KEYBOARD: + return; + case WLR_DRAG_GRAB_KEYBOARD_POINTER: icon->x = cursor->x; icon->y = cursor->y; - } else { + break; + case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = - wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->touch_id); + wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->drag->touch_id); if (point == NULL) { return; } @@ -292,9 +324,39 @@ static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { free(icon); } -static void handle_new_drag_icon(struct wl_listener *listener, void *data) { - struct sway_seat *seat = wl_container_of(listener, seat, new_drag_icon); - struct wlr_drag_icon *wlr_drag_icon = data; +static void handle_request_start_drag(struct wl_listener *listener, + void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, request_start_drag); + struct wlr_seat_request_start_drag_event *event = data; + + if (wlr_seat_validate_pointer_grab_serial(seat->wlr_seat, + event->origin, event->serial)) { + wlr_seat_start_pointer_drag(seat->wlr_seat, event->drag, event->serial); + return; + } + + struct wlr_touch_point *point; + if (wlr_seat_validate_touch_grab_serial(seat->wlr_seat, + event->origin, event->serial, &point)) { + wlr_seat_start_touch_drag(seat->wlr_seat, + event->drag, event->serial, point); + return; + } + + // TODO: tablet grabs + + sway_log(SWAY_DEBUG, "Ignoring start_drag request: " + "could not validate pointer or touch serial %" PRIu32, event->serial); + wlr_data_source_destroy(event->drag->source); +} + +static void handle_start_drag(struct wl_listener *listener, void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, start_drag); + struct wlr_drag *wlr_drag = data; + struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; + if (wlr_drag_icon == NULL) { + return; + } struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); if (icon == NULL) { @@ -317,7 +379,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) { wl_list_insert(&root->drag_icons, &icon->link); drag_icon_update_position(icon); - seatop_abort(seat); + seatop_begin_default(seat); } static void handle_request_set_selection(struct wl_listener *listener, @@ -393,8 +455,12 @@ struct sway_seat *seat_create(const char *seat_name) { wl_signal_add(&root->events.new_node, &seat->new_node); seat->new_node.notify = handle_new_node; - wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon); - seat->new_drag_icon.notify = handle_new_drag_icon; + wl_signal_add(&seat->wlr_seat->events.request_start_drag, + &seat->request_start_drag); + seat->request_start_drag.notify = handle_request_start_drag; + + wl_signal_add(&seat->wlr_seat->events.start_drag, &seat->start_drag); + seat->start_drag.notify = handle_start_drag; wl_signal_add(&seat->wlr_seat->events.request_set_selection, &seat->request_set_selection); @@ -409,6 +475,8 @@ struct sway_seat *seat_create(const char *seat_name) { wl_list_insert(&server.input->seats, &seat->link); + seatop_begin_default(seat); + return seat; } @@ -429,8 +497,8 @@ static void seat_update_capabilities(struct sway_seat *seat) { case WLR_INPUT_DEVICE_TABLET_TOOL: caps |= WL_SEAT_CAPABILITY_POINTER; break; - case WLR_INPUT_DEVICE_TABLET_PAD: case WLR_INPUT_DEVICE_SWITCH: + case WLR_INPUT_DEVICE_TABLET_PAD: break; } } @@ -517,6 +585,15 @@ static void seat_configure_keyboard(struct sway_seat *seat, } } +static void seat_configure_switch(struct sway_seat *seat, + struct sway_seat_device *seat_device) { + if (!seat_device->switch_device) { + sway_switch_create(seat, seat_device); + } + seat_apply_input_config(seat, seat_device); + sway_switch_configure(seat_device->switch_device); +} + static void seat_configure_touch(struct sway_seat *seat, struct sway_seat_device *sway_device) { wlr_cursor_attach_input_device(seat->cursor->cursor, @@ -558,6 +635,9 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_KEYBOARD: seat_configure_keyboard(seat, seat_device); break; + case WLR_INPUT_DEVICE_SWITCH: + seat_configure_switch(seat, seat_device); + break; case WLR_INPUT_DEVICE_TOUCH: seat_configure_touch(seat, seat_device); break; @@ -567,9 +647,6 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_TABLET_PAD: sway_log(SWAY_DEBUG, "TODO: configure tablet pad"); break; - case WLR_INPUT_DEVICE_SWITCH: - sway_log(SWAY_DEBUG, "TODO: configure switch device"); - break; } } @@ -653,8 +730,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { seat->cursor->xcursor_manager = wlr_xcursor_manager_create(cursor_theme, 24); if (sway_assert(seat->cursor->xcursor_manager, - "Cannot create XCursor manager for theme %s", - cursor_theme)) { + "Cannot create XCursor manager for theme")) { return; } } @@ -712,6 +788,18 @@ static void set_workspace(struct sway_seat *seat, if (seat->workspace == new_ws) { return; } + + if (seat->workspace) { + free(seat->prev_workspace_name); + seat->prev_workspace_name = malloc(strlen(seat->workspace->name) + 1); + if (!seat->prev_workspace_name) { + sway_log(SWAY_ERROR, "Unable to allocate previous workspace name"); + seat->prev_workspace_name = NULL; + } else { + strcpy(seat->prev_workspace_name, seat->workspace->name); + } + } + ipc_event_workspace(seat->workspace, new_ws, "focus"); seat->workspace = new_ws; } @@ -721,7 +809,13 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { wl_list_remove(&seat_node->link); wl_list_insert(&seat->focus_stack, &seat_node->link); node_set_dirty(node); - node_set_dirty(node_get_parent(node)); + + // If focusing a scratchpad container that is fullscreen global, parent + // will be NULL + struct sway_node *parent = node_get_parent(node); + if (parent) { + node_set_dirty(parent); + } } void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { @@ -747,7 +841,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } seat_send_unfocus(last_focus, seat); seat->has_focus = false; - update_debug_tree(); return; } @@ -777,7 +870,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } } - struct sway_output *new_output = new_workspace->output; + struct sway_output *new_output = + new_workspace ? new_workspace->output : NULL; if (last_workspace != new_workspace && new_output) { node_set_dirty(&new_output->node); @@ -821,7 +915,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } // Move sticky containers to new workspace - if (new_output_last_ws && new_workspace != new_output_last_ws) { + if (new_workspace && new_output_last_ws + && new_workspace != new_output_last_ws) { for (int i = 0; i < new_output_last_ws->floating->length; ++i) { struct sway_container *floater = new_output_last_ws->floating->items[i]; @@ -867,13 +962,11 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { seat->has_focus = true; - if (config->smart_gaps) { + if (config->smart_gaps && new_workspace) { // When smart gaps is on, gaps may change when the focus changes so // the workspace needs to be arranged arrange_workspace(new_workspace); } - - update_debug_tree(); } void seat_set_focus_container(struct sway_seat *seat, @@ -1050,7 +1143,7 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { } struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { - struct sway_node *focus = seat_get_focus(seat); + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); if (!focus) { return NULL; } @@ -1060,7 +1153,21 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { if (focus->type == N_WORKSPACE) { return focus->sway_workspace; } - return NULL; // unreachable + return NULL; // output doesn't have a workspace yet +} + +struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) { + struct sway_seat_node *current; + wl_list_for_each(current, &seat->focus_stack, link) { + struct sway_node *node = current->node; + if (node->type == N_CONTAINER && + node->sway_container->workspace) { + return node->sway_container->workspace; + } else if (node->type == N_WORKSPACE) { + return node->sway_workspace; + } + } + return NULL; } struct sway_container *seat_get_focused_container(struct sway_seat *seat) { @@ -1112,7 +1219,6 @@ struct seat_config *seat_get_config_by_name(const char *name) { void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { - seat->last_button = button; seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); } @@ -1124,11 +1230,13 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } if (config->mouse_warping == WARP_OUTPUT) { struct sway_output *output = node_get_output(focus); - struct wlr_box box; - output_get_box(output, &box); - if (wlr_box_contains_point(&box, - seat->cursor->cursor->x, seat->cursor->cursor->y)) { - return; + if (output) { + struct wlr_box box; + output_get_box(output, &box); + if (wlr_box_contains_point(&box, + seat->cursor->cursor->x, seat->cursor->cursor->y)) { + return; + } } } @@ -1143,34 +1251,42 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } } -bool seat_doing_seatop(struct sway_seat *seat) { - return seat->seatop_impl != NULL; -} - void seatop_unref(struct sway_seat *seat, struct sway_container *con) { - if (seat->seatop_impl && seat->seatop_impl->unref) { + if (seat->seatop_impl->unref) { seat->seatop_impl->unref(seat, con); } } -void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { - if (seat->seatop_impl && seat->seatop_impl->motion) { - seat->seatop_impl->motion(seat, time_msec); +void seatop_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->seatop_impl->button) { + seat->seatop_impl->button(seat, time_msec, device, button, state); } } -void seatop_finish(struct sway_seat *seat) { - if (seat->seatop_impl && seat->seatop_impl->finish) { - seat->seatop_impl->finish(seat); +void seatop_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { + if (seat->seatop_impl->motion) { + seat->seatop_impl->motion(seat, time_msec, dx, dy); } - free(seat->seatop_data); - seat->seatop_data = NULL; - seat->seatop_impl = NULL; } -void seatop_abort(struct sway_seat *seat) { - if (seat->seatop_impl && seat->seatop_impl->abort) { - seat->seatop_impl->abort(seat); +void seatop_axis(struct sway_seat *seat, struct wlr_event_pointer_axis *event) { + if (seat->seatop_impl->axis) { + seat->seatop_impl->axis(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); + } +} + +void seatop_end(struct sway_seat *seat) { + if (seat->seatop_impl && seat->seatop_impl->end) { + seat->seatop_impl->end(seat); } free(seat->seatop_data); seat->seatop_data = NULL; @@ -1179,7 +1295,11 @@ void seatop_abort(struct sway_seat *seat) { void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage) { - if (seat->seatop_impl && seat->seatop_impl->render) { + if (seat->seatop_impl->render) { seat->seatop_impl->render(seat, output, damage); } } + +bool seatop_allows_set_cursor(struct sway_seat *seat) { + return seat->seatop_impl->allow_set_cursor; +} diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c new file mode 100644 index 000000000..881384719 --- /dev/null +++ b/sway/input/seatop_default.c @@ -0,0 +1,622 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/tree/view.h" +#include "log.h" + +struct seatop_default_event { + struct sway_node *previous_node; + uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; + size_t pressed_button_count; +}; + +/*-----------------------------------------\ + * Functions shared by multiple callbacks / + *---------------------------------------*/ + +/** + * Determine if the edge of the given container is on the edge of the + * workspace/output. + */ +static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { + enum sway_container_layout layout = L_NONE; + switch (edge) { + case WLR_EDGE_TOP: + case WLR_EDGE_BOTTOM: + layout = L_VERT; + break; + case WLR_EDGE_LEFT: + case WLR_EDGE_RIGHT: + layout = L_HORIZ; + break; + case WLR_EDGE_NONE: + sway_assert(false, "Never reached"); + return false; + } + + // Iterate the parents until we find one with the layout we want, + // then check if the child has siblings between it and the edge. + while (cont) { + if (container_parent_layout(cont) == layout) { + list_t *siblings = container_get_siblings(cont); + int index = list_find(siblings, cont); + if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { + return false; + } + if (index < siblings->length - 1 && + (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { + return false; + } + } + cont = cont->parent; + } + return true; +} + +static enum wlr_edges find_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + if (!cont->view) { + return WLR_EDGE_NONE; + } + if (cont->border == B_NONE || !cont->border_thickness || + cont->border == B_CSD) { + return WLR_EDGE_NONE; + } + + enum wlr_edges edge = 0; + if (cursor->cursor->x < cont->x + cont->border_thickness) { + edge |= WLR_EDGE_LEFT; + } + if (cursor->cursor->y < cont->y + cont->border_thickness) { + edge |= WLR_EDGE_TOP; + } + if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { + edge |= WLR_EDGE_RIGHT; + } + if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { + edge |= WLR_EDGE_BOTTOM; + } + + return edge; +} + +/** + * If the cursor is over a _resizable_ edge, return the edge. + * Edges that can't be resized are edges of the workspace. + */ +static enum wlr_edges find_resize_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + enum wlr_edges edge = find_edge(cont, cursor); + if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { + return WLR_EDGE_NONE; + } + return edge; +} + +/** + * Return the mouse binding which matches modifier, click location, release, + * and pressed button state, otherwise return null. + */ +static struct sway_binding* get_active_mouse_binding( + struct seatop_default_event *e, list_t *bindings, uint32_t modifiers, + bool release, bool on_titlebar, bool on_border, bool on_content, + bool on_workspace, const char *identifier) { + uint32_t click_region = + ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | + ((on_border || on_workspace) ? BINDING_BORDER : 0) | + ((on_content || on_workspace) ? BINDING_CONTENTS : 0); + + struct sway_binding *current = NULL; + for (int i = 0; i < bindings->length; ++i) { + struct sway_binding *binding = bindings->items[i]; + if (modifiers ^ binding->modifiers || + e->pressed_button_count != (size_t)binding->keys->length || + release != (binding->flags & BINDING_RELEASE) || + !(click_region & binding->flags) || + (on_workspace && + (click_region & binding->flags) != click_region) || + (strcmp(binding->input, identifier) != 0 && + strcmp(binding->input, "*") != 0)) { + continue; + } + + bool match = true; + for (size_t j = 0; j < e->pressed_button_count; j++) { + uint32_t key = *(uint32_t *)binding->keys->items[j]; + if (key != e->pressed_buttons[j]) { + match = false; + break; + } + } + if (!match) { + continue; + } + + if (!current || strcmp(current->input, "*") == 0) { + current = binding; + if (strcmp(current->input, identifier) == 0) { + // If a binding is found for the exact input, quit searching + break; + } + } + } + return current; +} + +/** + * Remove a button (and duplicates) from the sorted list of currently pressed + * buttons. + */ +static void state_erase_button(struct seatop_default_event *e, + uint32_t button) { + size_t j = 0; + for (size_t i = 0; i < e->pressed_button_count; ++i) { + if (i > j) { + e->pressed_buttons[j] = e->pressed_buttons[i]; + } + if (e->pressed_buttons[i] != button) { + ++j; + } + } + while (e->pressed_button_count > j) { + --e->pressed_button_count; + e->pressed_buttons[e->pressed_button_count] = 0; + } +} + +/** + * Add a button to the sorted list of currently pressed buttons, if there + * is space. + */ +static void state_add_button(struct seatop_default_event *e, uint32_t button) { + if (e->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { + return; + } + size_t i = 0; + while (i < e->pressed_button_count && e->pressed_buttons[i] < button) { + ++i; + } + size_t j = e->pressed_button_count; + while (j > i) { + e->pressed_buttons[j] = e->pressed_buttons[j - 1]; + --j; + } + e->pressed_buttons[i] = button; + e->pressed_button_count++; +} + +static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, + struct sway_node *node, struct wlr_surface *surface, + double sx, double sy) { + struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; + if (surface) { + if (seat_is_input_allowed(cursor->seat, surface)) { + wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); + } + } else if (node && node->type == N_CONTAINER) { + // Try a node's resize edge + enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); + if (edge == WLR_EDGE_NONE) { + cursor_set_image(cursor, "left_ptr", NULL); + } else if (container_is_floating(node->sway_container)) { + cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); + } else { + if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { + cursor_set_image(cursor, "col-resize", NULL); + } else { + cursor_set_image(cursor, "row-resize", NULL); + } + } + } else { + cursor_set_image(cursor, "left_ptr", NULL); + } + + if (surface == NULL) { + wlr_seat_pointer_clear_focus(wlr_seat); + } +} + +/*----------------------------------\ + * Functions used by handle_button / + *--------------------------------*/ + +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + bool is_floating = cont && container_is_floating(cont); + bool is_floating_or_child = cont && container_is_floating_or_child(cont); + bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + enum wlr_edges resize_edge = edge ? + find_resize_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + bool on_titlebar = cont && !on_border && !surface; + + // Handle mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + + char *device_identifier = device ? input_device_get_identifier(device) + : strdup("*"); + struct sway_binding *binding = NULL; + if (state == WLR_BUTTON_PRESSED) { + state_add_button(e, button); + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, modifiers, false, + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); + } else { + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, modifiers, true, + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); + state_erase_button(e, button); + } + free(device_identifier); + if (binding) { + seat_execute_command(seat, binding); + return; + } + + // Handle clicking an empty workspace + if (node && node->type == N_WORKSPACE) { + seat_set_focus(seat, node); + return; + } + + // Handle clicking a layer surface + if (surface && wlr_surface_is_layer_surface(surface)) { + struct wlr_layer_surface_v1 *layer = + wlr_layer_surface_v1_from_wlr_surface(surface); + if (layer->current.keyboard_interactive) { + seat_set_focus_layer(seat, layer); + } + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + // Handle tiling resize via border + if (cont && resize_edge && button == BTN_LEFT && + state == WLR_BUTTON_PRESSED && !is_floating) { + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; + } + + // Handle tiling resize via mod + bool mod_pressed = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); + if (cont && !is_floating_or_child && mod_pressed && + state == WLR_BUTTON_PRESSED) { + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (button == btn_resize) { + edge = 0; + edge |= cursor->cursor->x > cont->x + cont->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->y + cont->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + + const char *image = NULL; + if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { + image = "nw-resize"; + } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { + image = "ne-resize"; + } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { + image = "se-resize"; + } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { + image = "sw-resize"; + } + cursor_set_image(seat->cursor, image, NULL); + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; + } + } + + // 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 && state == WLR_BUTTON_PRESSED && + (mod_pressed || on_titlebar)) { + while (cont->parent) { + cont = cont->parent; + } + seat_set_focus_container(seat, cont); + seatop_begin_move_floating(seat, cont); + return; + } + } + + // Handle beginning floating resize + if (cont && is_floating_or_child && !is_fullscreen_or_child && + state == WLR_BUTTON_PRESSED) { + // Via border + if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seatop_begin_resize_floating(seat, cont, resize_edge); + return; + } + + // Via mod+click + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (mod_pressed && button == btn_resize) { + struct sway_container *floater = cont; + while (floater->parent) { + floater = floater->parent; + } + edge = 0; + edge |= cursor->cursor->x > floater->x + floater->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > floater->y + floater->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seatop_begin_resize_floating(seat, floater, edge); + return; + } + } + + // Handle moving a tiling container + if (config->tiling_drag && (mod_pressed || on_titlebar) && + state == WLR_BUTTON_PRESSED && !is_floating_or_child && + cont && cont->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 (!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; + } + + // Handle clicking a container surface or decorations + if (cont) { + node = seat_get_focus_inactive(seat, &cont->node); + seat_set_focus(seat, node); + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + seat_pointer_notify_button(seat, time_msec, button, state); +} + +/*----------------------------------\ + * Functions used by handle_motion / + *--------------------------------*/ + +static void check_focus_follows_mouse(struct sway_seat *seat, + struct seatop_default_event *e, struct sway_node *hovered_node) { + struct sway_node *focus = seat_get_focus(seat); + + // If a workspace node is hovered (eg. in the gap area), only set focus if + // the workspace is on a different output to the previous focus. + if (focus && hovered_node->type == N_WORKSPACE) { + struct sway_output *focused_output = node_get_output(focus); + struct sway_output *hovered_output = node_get_output(hovered_node); + if (hovered_output != focused_output) { + seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); + } + return; + } + + // This is where we handle the common case. We don't want to focus inactive + // tabs, hence the view_is_visible check. + if (node_is_view(hovered_node) && + view_is_visible(hovered_node->sway_container->view)) { + // e->previous_node is the node which the cursor was over previously. + // If focus_follows_mouse is yes and the cursor got over the view due + // to, say, a workspace switch, we don't want to set the focus. + // But if focus_follows_mouse is "always", we do. + if (hovered_node != e->previous_node || + config->focus_follows_mouse == FOLLOWS_ALWAYS) { + seat_set_focus(seat, hovered_node); + } + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + + if (node && config->focus_follows_mouse != FOLLOWS_NO) { + check_focus_follows_mouse(seat, e, node); + } + + cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); + if (surface && seat_is_input_allowed(cursor->seat, surface)) { + wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); + } + + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } + } + + e->previous_node = node; +} + +/*--------------------------------\ + * Functions used by handle_axis / + *------------------------------*/ + +static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { + switch (event->orientation) { + case WLR_AXIS_ORIENTATION_VERTICAL: + return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; + case WLR_AXIS_ORIENTATION_HORIZONTAL: + return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; + default: + sway_log(SWAY_DEBUG, "Unknown axis orientation"); + return 0; + } +} + +static void handle_axis(struct sway_seat *seat, + struct wlr_event_pointer_axis *event) { + struct sway_input_device *input_device = + event->device ? event->device->data : NULL; + struct input_config *ic = + input_device ? input_device_get_config(input_device) : NULL; + struct sway_cursor *cursor = seat->cursor; + struct seatop_default_event *e = seat->seatop_data; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_titlebar = cont && !on_border && !surface; + bool on_titlebar_border = cont && on_border && + cursor->cursor->y < cont->content_y; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + float scroll_factor = + (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; + + bool handled = false; + + // Gather information needed for mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + struct wlr_input_device *device = + input_device ? input_device->wlr_device : NULL; + char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); + uint32_t button = wl_axis_to_button(event); + + // Handle mouse bindings - x11 mouse buttons 4-7 - press event + struct sway_binding *binding = NULL; + state_add_button(e, button); + binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, + modifiers, false, on_titlebar, on_border, on_contents, on_workspace, + dev_id); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + + // Scrolling on a tabbed or stacked title bar (handled as press event) + if (!handled && (on_titlebar || on_titlebar_border)) { + enum sway_container_layout layout = container_parent_layout(cont); + if (layout == L_TABBED || layout == L_STACKED) { + struct sway_node *tabcontainer = node_get_parent(node); + struct sway_node *active = + 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); + if (desired < 0) { + desired = 0; + } else if (desired >= siblings->length) { + desired = siblings->length - 1; + } + struct sway_node *old_focus = seat_get_focus(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); + if (node_has_ancestor(old_focus, tabcontainer)) { + seat_set_focus(seat, new_focus); + } else { + // Scrolling when focus is not in the tabbed container at all + seat_set_raw_focus(seat, new_sibling); + seat_set_raw_focus(seat, new_focus); + seat_set_raw_focus(seat, old_focus); + } + handled = true; + } + } + + // Handle mouse bindings - x11 mouse buttons 4-7 - release event + binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, + modifiers, true, on_titlebar, on_border, on_contents, on_workspace, + dev_id); + state_erase_button(e, button); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + free(dev_id); + + if (!handled) { + wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, + event->orientation, scroll_factor * event->delta, + round(scroll_factor * event->delta_discrete), event->source); + } +} + +/*----------------------------------\ + * Functions used by handle_rebase / + *--------------------------------*/ + +static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + struct wlr_surface *surface = NULL; + double sx = 0.0, sy = 0.0; + e->previous_node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + cursor_do_rebase(cursor, time_msec, e->previous_node, surface, sx, sy); +} + +static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, + .motion = handle_motion, + .axis = handle_axis, + .rebase = handle_rebase, + .allow_set_cursor = true, +}; + +void seatop_begin_default(struct sway_seat *seat) { + seatop_end(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); +} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index c2256c9a1..95ea7cbb2 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -3,15 +3,26 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/tree/view.h" +#include "log.h" struct seatop_down_event { struct sway_container *con; double ref_lx, ref_ly; // cursor's x/y at start of op double ref_con_lx, ref_con_ly; // container's x/y at start of op - bool moved; }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + seat_pointer_notify_button(seat, time_msec, button, state); + + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_down_event *e = seat->seatop_data; struct sway_container *con = e->con; if (seat_is_input_allowed(seat, con->view->surface)) { @@ -21,48 +32,25 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { double sy = e->ref_con_ly + moved_y; wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); } - e->moved = true; -} - -static void handle_finish(struct sway_seat *seat) { - struct seatop_down_event *e = seat->seatop_data; - struct sway_cursor *cursor = seat->cursor; - // Set the cursor's previous coords to the x/y at the start of the - // operation, so the container change will be detected if using - // focus_follows_mouse and the cursor moved off the original container - // during the operation. - cursor->previous.x = e->ref_lx; - cursor->previous.y = e->ref_ly; - if (e->moved) { - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy); - } -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_down_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, + .allow_set_cursor = true, }; -void seatop_begin_down(struct sway_seat *seat, - struct sway_container *con, uint32_t button, int sx, int sy) { - seatop_abort(seat); +void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, + uint32_t time_msec, int sx, int sy) { + seatop_end(seat); struct seatop_down_event *e = calloc(1, sizeof(struct seatop_down_event)); @@ -74,11 +62,9 @@ void seatop_begin_down(struct sway_seat *seat, e->ref_ly = seat->cursor->cursor->y; e->ref_con_lx = sx; e->ref_con_ly = sy; - e->moved = false; seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); } diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 08e3a5a4e..8f133c36f 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -8,45 +8,44 @@ struct seatop_move_floating_event { struct sway_container *con; }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { - struct seatop_move_floating_event *e = seat->seatop_data; - desktop_damage_whole_container(e->con); - container_floating_translate(e->con, - seat->cursor->cursor->x - seat->cursor->previous.x, - seat->cursor->cursor->y - seat->cursor->previous.y); - desktop_damage_whole_container(e->con); +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + struct seatop_move_floating_event *e = seat->seatop_data; + + // We "move" the container to its own location + // so it discovers its output again. + container_floating_move_to(e->con, e->con->x, e->con->y); + + seatop_begin_default(seat); + } } -static void handle_finish(struct sway_seat *seat) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_move_floating_event *e = seat->seatop_data; - - // We "move" the container to its own location - // so it discovers its output again. - container_floating_move_to(e->con, e->con->x, e->con->y); - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); + desktop_damage_whole_container(e->con); + container_floating_translate(e->con, dx, dy); + desktop_damage_whole_container(e->con); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_move_floating_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_move_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_abort(seat); + struct sway_container *con) { + seatop_end(seat); struct seatop_move_floating_event *e = calloc(1, sizeof(struct seatop_move_floating_event)); @@ -57,9 +56,9 @@ void seatop_begin_move_floating(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); cursor_set_image(seat->cursor, "grab", NULL); + wlr_seat_pointer_clear_focus(seat->wlr_seat); } diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 1e548f5ae..7c31e695a 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -37,7 +37,7 @@ static void handle_render(struct sway_seat *seat, struct wlr_box box; memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); scale_box(&box, output->wlr_output->scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } } @@ -108,7 +108,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } if (node->type == N_WORKSPACE) { - // Emtpy workspace + // Empty workspace e->target_node = node; e->target_edge = WLR_EDGE_NONE; workspace_get_box(node->sway_workspace, &e->drop_box); @@ -164,7 +164,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Use the hovered view - but we must be over the actual surface con = node->sway_container; - if (!con->view->surface || node == &e->con->node + if (!con->view || !con->view->surface || node == &e->con->node || node_has_ancestor(node, &e->con->node)) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; @@ -206,7 +206,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { desktop_damage_box(&e->drop_box); } -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_move_tiling_event *e = seat->seatop_data; if (e->threshold_reached) { handle_motion_postthreshold(seat); @@ -215,10 +216,6 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static bool is_parallel(enum sway_container_layout layout, enum wlr_edges edge) { bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED; @@ -226,11 +223,17 @@ static bool is_parallel(enum sway_container_layout layout, return layout_is_horiz == edge_is_horiz; } -static void handle_finish(struct sway_seat *seat) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count != 0) { + return; + } + struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->target_node) { - handle_abort(seat); + seatop_begin_default(seat); return; } @@ -287,7 +290,7 @@ static void handle_finish(struct sway_seat *seat) { arrange_workspace(new_ws); } - cursor_set_image(seat->cursor, "left_ptr", NULL); + seatop_begin_default(seat); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { @@ -296,21 +299,20 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { e->target_node = NULL; } if (e->con == con) { // The container being moved - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, .render = handle_render, }; void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_abort(seat); + struct sway_container *con) { + seatop_end(seat); struct seatop_move_tiling_event *e = calloc(1, sizeof(struct seatop_move_tiling_event)); @@ -323,14 +325,14 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); + wlr_seat_pointer_clear_focus(seat->wlr_seat); } void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_begin_move_tiling_threshold(seat, con, button); + struct sway_container *con) { + seatop_begin_move_tiling_threshold(seat, con); struct seatop_move_tiling_event *e = seat->seatop_data; if (e) { e->threshold_reached = true; diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 12851b406..3d95295c5 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -17,42 +17,16 @@ struct seatop_resize_floating_event { double ref_con_lx, ref_con_ly; // container's x/y at start of op }; -static void calculate_floating_constraints(struct sway_container *con, - int *min_width, int *max_width, int *min_height, int *max_height) { - if (config->floating_minimum_width == -1) { // no minimum - *min_width = 0; - } else if (config->floating_minimum_width == 0) { // automatic - *min_width = 75; - } else { - *min_width = config->floating_minimum_width; - } - - if (config->floating_minimum_height == -1) { // no minimum - *min_height = 0; - } else if (config->floating_minimum_height == 0) { // automatic - *min_height = 50; - } else { - *min_height = config->floating_minimum_height; - } - - if (config->floating_maximum_width == -1) { // no maximum - *max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - *max_width = con->workspace->width; - } else { - *max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1) { // no maximum - *max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - *max_height = con->workspace->height; - } else { - *max_height = config->floating_maximum_height; +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); } } -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_resize_floating_event *e = seat->seatop_data; struct sway_container *con = e->con; enum wlr_edges edge = e->edge; @@ -85,7 +59,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { double width = e->ref_width + grow_width; double height = e->ref_height + grow_height; int min_width, max_width, min_height, max_height; - calculate_floating_constraints(con, &min_width, &max_width, + floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); width = fmax(min_width, fmin(width, max_width)); height = fmax(min_height, fmin(height, max_height)); @@ -142,31 +116,22 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { arrange_container(con); } -static void handle_finish(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_resize_floating_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_resize_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge) { - seatop_abort(seat); + struct sway_container *con, enum wlr_edges edge) { + seatop_end(seat); struct seatop_resize_floating_event *e = calloc(1, sizeof(struct seatop_resize_floating_event)); @@ -189,11 +154,11 @@ void seatop_begin_resize_floating(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); const char *image = edge == WLR_EDGE_NONE ? "se-resize" : wlr_xcursor_get_resize_name(edge); cursor_set_image(seat->cursor, image, NULL); + wlr_seat_pointer_clear_focus(seat->wlr_seat); } diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index cb0f723d0..020bba1b1 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -19,7 +19,16 @@ struct seatop_resize_tiling_event { double v_con_orig_height; // height of the vertical ancestor at start }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_resize_tiling_event *e = seat->seatop_data; int amount_x = 0; int amount_y = 0; @@ -49,31 +58,22 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_finish(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_resize_tiling_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_resize_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge) { - seatop_abort(seat); + struct sway_container *con, enum wlr_edges edge) { + seatop_end(seat); struct seatop_resize_tiling_event *e = calloc(1, sizeof(struct seatop_resize_tiling_event)); @@ -105,5 +105,6 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; + + wlr_seat_pointer_clear_focus(seat->wlr_seat); } diff --git a/sway/input/switch.c b/sway/input/switch.c new file mode 100644 index 000000000..d825c5c31 --- /dev/null +++ b/sway/input/switch.c @@ -0,0 +1,95 @@ +#include "sway/config.h" +#include "sway/desktop/transaction.h" +#include "sway/input/switch.h" +#include +#include "log.h" + +struct sway_switch *sway_switch_create(struct sway_seat *seat, + struct sway_seat_device *device) { + struct sway_switch *switch_device = + calloc(1, sizeof(struct sway_switch)); + if (!sway_assert(switch_device, "could not allocate switch")) { + return NULL; + } + device->switch_device = switch_device; + switch_device->seat_device = device; + wl_list_init(&switch_device->switch_toggle.link); + sway_log(SWAY_DEBUG, "Allocated switch for device"); + + return switch_device; +} + +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 sway_seat* seat = sway_switch->seat_device->sway_seat; + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct wlr_input_device *wlr_device = + sway_switch->seat_device->input_device->wlr_device; + + wlr_idle_notify_activity(server.idle, wlr_seat); + bool input_inhibited = seat->exclusive_client != NULL; + + char *device_identifier = input_device_get_identifier(wlr_device); + + struct wlr_event_switch_toggle *event = data; + enum wlr_switch_type type = event->switch_type; + enum wlr_switch_state state = event->switch_state; + sway_log(SWAY_DEBUG, "%s: type %d state %d", device_identifier, type, state); + + list_t *bindings = config->current_mode->switch_bindings; + struct sway_switch_binding *matched_binding = NULL; + for (int i = 0; i < bindings->length; ++i) { + struct sway_switch_binding *binding = bindings->items[i]; + if (binding->type != type) { + continue; + } + if (binding->state != WLR_SWITCH_STATE_TOGGLE && + binding->state != state) { + continue; + } + bool binding_locked = binding->flags & BINDING_LOCKED; + if (!binding_locked && input_inhibited) { + continue; + } + + matched_binding = binding; + + if (binding_locked == input_inhibited) { + break; + } + } + + if (matched_binding) { + struct sway_binding *dummy_binding = + calloc(1, sizeof(struct sway_binding)); + dummy_binding->type = BINDING_SWITCH; + dummy_binding->flags = matched_binding->flags; + dummy_binding->command = matched_binding->command; + + seat_execute_command(seat, dummy_binding); + free(dummy_binding); + } + + transaction_commit_dirty(); + + free(device_identifier); +} + +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, + &sway_switch->switch_toggle); + sway_switch->switch_toggle.notify = handle_switch_toggle; + sway_log(SWAY_DEBUG, "Configured switch for device"); +} + +void sway_switch_destroy(struct sway_switch *sway_switch) { + if (!sway_switch) { + return; + } + wl_list_remove(&sway_switch->switch_toggle.link); + free(sway_switch); +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index a2ab2bba8..b9abc47f2 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -85,24 +85,6 @@ static const char *ipc_json_output_transform_description(enum wl_output_transfor return NULL; } -static const char *ipc_json_device_type_description(struct sway_input_device *device) { - switch (device->wlr_device->type) { - case WLR_INPUT_DEVICE_POINTER: - return "pointer"; - case WLR_INPUT_DEVICE_KEYBOARD: - return "keyboard"; - case WLR_INPUT_DEVICE_TOUCH: - return "touch"; - case WLR_INPUT_DEVICE_TABLET_TOOL: - return "tablet_tool"; - case WLR_INPUT_DEVICE_TABLET_PAD: - return "tablet_pad"; - case WLR_INPUT_DEVICE_SWITCH: - return "switch"; - } - return "unknown"; -} - json_object *ipc_json_get_version(void) { int major = 0, minor = 0, patch = 0; json_object *version = json_object_new_object(); @@ -180,6 +162,8 @@ static void ipc_json_describe_output(struct sway_output *output, struct wlr_output *wlr_output = output->wlr_output; json_object_object_add(object, "type", json_object_new_string("output")); json_object_object_add(object, "active", json_object_new_boolean(true)); + json_object_object_add(object, "dpms", + json_object_new_boolean(output->wlr_output->enabled)); json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "layout", json_object_new_string("output")); json_object_object_add(object, "orientation", @@ -251,13 +235,38 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *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_object_add(object, "modes", json_object_new_array()); + + 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, "current_workspace", NULL); + + json_object *rect_object = json_object_new_object(); + json_object_object_add(rect_object, "x", json_object_new_int(0)); + json_object_object_add(rect_object, "y", json_object_new_int(0)); + json_object_object_add(rect_object, "width", json_object_new_int(0)); + json_object_object_add(rect_object, "height", json_object_new_int(0)); + json_object_object_add(object, "rect", rect_object); json_object_object_add(object, "percent", NULL); @@ -340,6 +349,43 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object_object_add(object, "floating_nodes", floating_array); } +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; + if (((!tab_or_stack || container_is_floating(c)) && + c->current.border != B_NORMAL) || + c->fullscreen_mode != FULLSCREEN_NONE || + c->workspace == NULL) { + deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; + return; + } + + if (c->parent) { + deco_rect->x = c->x - c->parent->x; + deco_rect->y = c->y - c->parent->y; + } else { + deco_rect->x = c->x - c->workspace->x; + deco_rect->y = c->y - c->workspace->y; + } + deco_rect->width = c->width; + deco_rect->height = container_titlebar_height(); + + if (!container_is_floating(c)) { + if (parent_layout == L_TABBED) { + deco_rect->width = c->parent + ? c->parent->width / c->parent->children->length + : c->workspace->width / c->workspace->tiling->length; + deco_rect->x += deco_rect->width * container_sibling_index(c); + } else if (parent_layout == L_STACKED) { + if (!c->view) { + size_t siblings = container_get_siblings(c)->length; + deco_rect->y -= deco_rect->height * siblings; + } + deco_rect->y += deco_rect->height * container_sibling_index(c); + } + } +} + static void ipc_json_describe_view(struct sway_container *c, json_object *object) { json_object_object_add(object, "pid", json_object_new_int(c->view->pid)); @@ -367,15 +413,6 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); - struct wlr_box deco_box = {0, 0, 0, 0}; - - if (c->current.border == B_NORMAL) { - deco_box.width = c->width; - deco_box.height = c->content_y - c->y; - } - - json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); - struct wlr_box geometry = {0, 0, c->view->natural_width, c->view->natural_height}; json_object_object_add(object, "geometry", ipc_json_create_rect(&geometry)); @@ -455,6 +492,10 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o json_object_new_int(c->current.border_thickness)); json_object_object_add(object, "floating_nodes", json_object_new_array()); + struct wlr_box deco_box = {0, 0, 0, 0}; + get_deco_rect(c, &deco_box); + json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); + if (c->view) { ipc_json_describe_view(c, object); } @@ -495,6 +536,16 @@ json_object *ipc_json_describe_node(struct sway_node *node) { struct wlr_box box; node_get_box(node, &box); + if (node->type == N_CONTAINER) { + struct wlr_box deco_rect = {0, 0, 0, 0}; + get_deco_rect(node->sway_container, &deco_rect); + size_t count = 1; + if (container_parent_layout(node->sway_container) == L_STACKED) { + count = container_get_siblings(node->sway_container)->length; + } + box.y += deco_rect.height * count; + box.height -= deco_rect.height * count; + } json_object *focus = json_object_new_array(); struct focus_inactive_data data = { @@ -569,6 +620,187 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { return object; } +static json_object *describe_libinput_device(struct libinput_device *device) { + json_object *object = json_object_new_object(); + + const char *events = "unknown"; + switch (libinput_device_config_send_events_get_mode(device)) { + case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: + events = "enabled"; + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: + events = "disabled_on_external_mouse"; + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: + events = "disabled"; + break; + } + json_object_object_add(object, "send_events", + json_object_new_string(events)); + + if (libinput_device_config_tap_get_finger_count(device) > 0) { + const char *tap = "unknown"; + switch (libinput_device_config_tap_get_enabled(device)) { + case LIBINPUT_CONFIG_TAP_ENABLED: + tap = "enabled"; + break; + case LIBINPUT_CONFIG_TAP_DISABLED: + tap = "disabled"; + break; + } + json_object_object_add(object, "tap", json_object_new_string(tap)); + + const char *button_map = "unknown"; + switch (libinput_device_config_tap_get_button_map(device)) { + case LIBINPUT_CONFIG_TAP_MAP_LRM: + button_map = "lrm"; + break; + case LIBINPUT_CONFIG_TAP_MAP_LMR: + button_map = "lmr"; + break; + } + json_object_object_add(object, "tap_button_map", + json_object_new_string(button_map)); + + const char* drag = "unknown"; + switch (libinput_device_config_tap_get_drag_enabled(device)) { + case LIBINPUT_CONFIG_DRAG_ENABLED: + drag = "enabled"; + break; + case LIBINPUT_CONFIG_DRAG_DISABLED: + drag = "disabled"; + break; + } + json_object_object_add(object, "tap_drag", + json_object_new_string(drag)); + + const char *drag_lock = "unknown"; + switch (libinput_device_config_tap_get_drag_lock_enabled(device)) { + case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED: + drag_lock = "enabled"; + break; + case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED: + drag_lock = "disabled"; + break; + } + json_object_object_add(object, "tap_drag_lock", + json_object_new_string(drag_lock)); + } + + if (libinput_device_config_accel_is_available(device)) { + double accel = libinput_device_config_accel_get_speed(device); + json_object_object_add(object, "accel_speed", + json_object_new_double(accel)); + + const char *accel_profile = "unknown"; + switch (libinput_device_config_accel_get_profile(device)) { + case LIBINPUT_CONFIG_ACCEL_PROFILE_NONE: + accel_profile = "none"; + break; + case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT: + accel_profile = "flat"; + break; + case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: + accel_profile = "adaptive"; + break; + } + json_object_object_add(object, "accel_profile", + json_object_new_string(accel_profile)); + } + + if (libinput_device_config_scroll_has_natural_scroll(device)) { + const char *natural_scroll = "disabled"; + if (libinput_device_config_scroll_get_natural_scroll_enabled(device)) { + natural_scroll = "enabled"; + } + json_object_object_add(object, "natural_scroll", + json_object_new_string(natural_scroll)); + } + + if (libinput_device_config_left_handed_is_available(device)) { + const char *left_handed = "disabled"; + if (libinput_device_config_left_handed_get(device) != 0) { + left_handed = "enabled"; + } + json_object_object_add(object, "left_handed", + json_object_new_string(left_handed)); + } + + uint32_t click_methods = libinput_device_config_click_get_methods(device); + if ((click_methods & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) != 0) { + const char *click_method = "unknown"; + switch (libinput_device_config_click_get_method(device)) { + case LIBINPUT_CONFIG_CLICK_METHOD_NONE: + click_method = "none"; + break; + case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS: + click_method = "button_areas"; + break; + case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER: + click_method = "clickfinger"; + break; + } + json_object_object_add(object, "click_method", + json_object_new_string(click_method)); + } + + if (libinput_device_config_middle_emulation_is_available(device)) { + const char *middle_emulation = "unknown"; + switch (libinput_device_config_middle_emulation_get_enabled(device)) { + case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: + middle_emulation = "enabled"; + break; + case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED: + middle_emulation = "disabled"; + break; + } + json_object_object_add(object, "middle_emulation", + json_object_new_string(middle_emulation)); + } + + uint32_t scroll_methods = libinput_device_config_scroll_get_methods(device); + if ((scroll_methods & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) != 0) { + const char *scroll_method = "unknown"; + switch (libinput_device_config_scroll_get_method(device)) { + case LIBINPUT_CONFIG_SCROLL_NO_SCROLL: + scroll_method = "none"; + break; + case LIBINPUT_CONFIG_SCROLL_2FG: + scroll_method = "two_finger"; + break; + case LIBINPUT_CONFIG_SCROLL_EDGE: + scroll_method = "edge"; + break; + case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN: + scroll_method = "on_button_down"; + break; + } + json_object_object_add(object, "scroll_method", + json_object_new_string(scroll_method)); + + if ((scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) != 0) { + uint32_t button = libinput_device_config_scroll_get_button(device); + json_object_object_add(object, "scroll_button", + json_object_new_int(button)); + } + } + + if (libinput_device_config_dwt_is_available(device)) { + const char *dwt = "unknown"; + switch (libinput_device_config_dwt_get_enabled(device)) { + case LIBINPUT_CONFIG_DWT_ENABLED: + dwt = "enabled"; + break; + case LIBINPUT_CONFIG_DWT_DISABLED: + dwt = "disabled"; + break; + } + json_object_object_add(object, "dwt", json_object_new_string(dwt)); + } + + return object; +} + json_object *ipc_json_describe_input(struct sway_input_device *device) { if (!(sway_assert(device, "Device must not be null"))) { return NULL; @@ -586,7 +818,7 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_int(device->wlr_device->product)); json_object_object_add(object, "type", json_object_new_string( - ipc_json_device_type_description(device))); + input_device_get_type(device))); if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { struct wlr_keyboard *keyboard = device->wlr_device->keyboard; @@ -612,21 +844,8 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { if (wlr_input_device_is_libinput(device->wlr_device)) { struct libinput_device *libinput_dev; libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); - - const char *events = "unknown"; - switch (libinput_device_config_send_events_get_mode(libinput_dev)) { - case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: - events = "enabled"; - break; - case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: - events = "disabled_on_external_mouse"; - break; - case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: - events = "disabled"; - break; - } - json_object_object_add(object, "libinput_send_events", - json_object_new_string(events)); + json_object_object_add(object, "libinput", + describe_libinput_device(libinput_dev)); } return object; diff --git a/sway/ipc-server.c b/sway/ipc-server.c index df57cba51..ca1c1b120 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -47,13 +47,14 @@ struct ipc_client { struct wl_event_source *writable_event_source; struct sway_server *server; int fd; - uint32_t payload_length; uint32_t security_policy; - enum ipc_command_type current_command; enum ipc_command_type subscribed_events; size_t write_buffer_len; size_t write_buffer_size; char *write_buffer; + // The following are for storing data between event_loop calls + uint32_t pending_length; + enum ipc_command_type pending_type; }; struct sockaddr_un *ipc_user_sockaddr(void); @@ -61,8 +62,10 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data); int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); void ipc_client_disconnect(struct ipc_client *client); -void ipc_client_handle_command(struct ipc_client *client); -bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); +void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, + enum ipc_command_type payload_type); +bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, + const char *payload, uint32_t payload_length); static void handle_display_destroy(struct wl_listener *listener, void *data) { if (ipc_event_source) { @@ -178,7 +181,7 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { return 0; } client->server = server; - client->payload_length = 0; + client->pending_length = 0; client->fd = client_fd; client->subscribed_events = 0; client->event_source = wl_event_loop_add_fd(server->wl_event_loop, @@ -224,9 +227,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { } // Wait for the rest of the command payload in case the header has already been read - if (client->payload_length > 0) { - if ((uint32_t)read_available >= client->payload_length) { - ipc_client_handle_command(client); + if (client->pending_length > 0) { + if ((uint32_t)read_available >= client->pending_length) { + // Reset pending values. + uint32_t pending_length = client->pending_length; + enum ipc_command_type pending_type = client->pending_type; + client->pending_length = 0; + ipc_client_handle_command(client, pending_length, pending_type); } return 0; } @@ -251,11 +258,15 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { return 0; } - memcpy(&client->payload_length, &buf32[0], sizeof(buf32[0])); - memcpy(&client->current_command, &buf32[1], sizeof(buf32[1])); + memcpy(&client->pending_length, &buf32[0], sizeof(buf32[0])); + memcpy(&client->pending_type, &buf32[1], sizeof(buf32[1])); - if (read_available - received >= (long)client->payload_length) { - ipc_client_handle_command(client); + if (read_available - received >= (long)client->pending_length) { + // Reset pending values. + uint32_t pending_length = client->pending_length; + enum ipc_command_type pending_type = client->pending_type; + client->pending_length = 0; + ipc_client_handle_command(client, pending_length, pending_type); } return 0; @@ -278,8 +289,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) if ((client->subscribed_events & event_mask(event)) == 0) { continue; } - client->current_command = event; - if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { + if (!ipc_send_reply(client, event, json_string, + (uint32_t)strlen(json_string))) { sway_log_errno(SWAY_INFO, "Unable to send reply to IPC client"); /* ipc_send_reply destroys client on error, which also * removes it from the list, so we need to process @@ -567,20 +578,21 @@ static void ipc_get_marks_callback(struct sway_container *con, void *data) { } } -void ipc_client_handle_command(struct ipc_client *client) { +void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, + enum ipc_command_type payload_type) { if (!sway_assert(client != NULL, "client != NULL")) { return; } - char *buf = malloc(client->payload_length + 1); + char *buf = malloc(payload_length + 1); if (!buf) { sway_log_errno(SWAY_INFO, "Unable to allocate IPC payload"); ipc_client_disconnect(client); return; } - if (client->payload_length > 0) { + if (payload_length > 0) { // Payload should be fully available - ssize_t received = recv(client->fd, buf, client->payload_length, 0); + ssize_t received = recv(client->fd, buf, payload_length, 0); if (received == -1) { sway_log_errno(SWAY_INFO, "Unable to receive payload from IPC client"); @@ -589,16 +601,15 @@ void ipc_client_handle_command(struct ipc_client *client) { return; } } - buf[client->payload_length] = '\0'; + buf[payload_length] = '\0'; - bool client_valid = true; - switch (client->current_command) { + switch (payload_type) { case IPC_COMMAND: { char *line = strtok(buf, "\n"); while (line) { size_t line_length = strlen(line); - if (line + line_length >= buf + client->payload_length) { + if (line + line_length >= buf + payload_length) { break; } line[line_length] = ';'; @@ -609,7 +620,7 @@ void ipc_client_handle_command(struct ipc_client *client) { transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); int length = strlen(json); - client_valid = ipc_send_reply(client, json, (uint32_t)length); + ipc_send_reply(client, payload_type, json, (uint32_t)length); free(json); while (res_list->length) { struct cmd_results *results = res_list->items[0]; @@ -623,7 +634,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_SEND_TICK: { ipc_event_tick(buf); - ipc_send_reply(client, "{\"success\": true}", 17); + ipc_send_reply(client, payload_type, "{\"success\": true}", 17); goto exit_cleanup; } @@ -644,6 +655,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_object_add(output_json, "focused", json_object_new_boolean(focused)); + const char *subpixel = sway_wl_output_subpixel_to_string(output->wlr_output->subpixel); + json_object_object_add(output_json, "subpixel_hinting", json_object_new_string(subpixel)); json_object_array_add(outputs, output_json); } struct sway_output *output; @@ -654,8 +667,8 @@ void ipc_client_handle_command(struct ipc_client *client) { } } const char *json_string = json_object_to_json_string(outputs); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(outputs); // free goto exit_cleanup; } @@ -665,8 +678,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *workspaces = json_object_new_array(); root_for_each_workspace(ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(workspaces); // free goto exit_cleanup; } @@ -677,7 +690,7 @@ void ipc_client_handle_command(struct ipc_client *client) { struct json_object *request = json_tokener_parse(buf); if (request == NULL || !json_object_is_type(request, json_type_array)) { const char msg[] = "{\"success\": false}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); sway_log(SWAY_INFO, "Failed to parse subscribe request"); goto exit_cleanup; } @@ -705,7 +718,7 @@ void ipc_client_handle_command(struct ipc_client *client) { is_tick = true; } else { const char msg[] = "{\"success\": false}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); json_object_put(request); sway_log(SWAY_INFO, "Unsupported event type in subscribe request"); goto exit_cleanup; @@ -714,11 +727,11 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_put(request); const char msg[] = "{\"success\": true}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); if (is_tick) { - client->current_command = IPC_EVENT_TICK; const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}"; - ipc_send_reply(client, tickmsg, strlen(tickmsg)); + ipc_send_reply(client, IPC_EVENT_TICK, tickmsg, + strlen(tickmsg)); } goto exit_cleanup; } @@ -731,8 +744,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(inputs, ipc_json_describe_input(device)); } const char *json_string = json_object_to_json_string(inputs); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(inputs); // free goto exit_cleanup; } @@ -745,8 +758,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(seats, ipc_json_describe_seat(seat)); } const char *json_string = json_object_to_json_string(seats); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(seats); // free goto exit_cleanup; } @@ -755,8 +768,8 @@ void ipc_client_handle_command(struct ipc_client *client) { { json_object *tree = ipc_json_describe_node_recursive(&root->node); const char *json_string = json_object_to_json_string(tree); - client_valid = - ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(tree); goto exit_cleanup; } @@ -766,8 +779,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *marks = json_object_new_array(); root_for_each_container(ipc_get_marks_callback, marks); const char *json_string = json_object_to_json_string(marks); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(marks); goto exit_cleanup; } @@ -776,8 +789,8 @@ void ipc_client_handle_command(struct ipc_client *client) { { json_object *version = ipc_json_get_version(); const char *json_string = json_object_to_json_string(version); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(version); // free goto exit_cleanup; } @@ -792,9 +805,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(bars, json_object_new_string(bar->id)); } const char *json_string = json_object_to_json_string(bars); - client_valid = - ipc_send_reply(client, json_string, - (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(bars); // free } else { // Send particular bar's details @@ -808,15 +820,14 @@ void ipc_client_handle_command(struct ipc_client *client) { } if (!bar) { const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; - client_valid = - ipc_send_reply(client, error, (uint32_t)strlen(error)); + ipc_send_reply(client, payload_type, error, + (uint32_t)strlen(error)); goto exit_cleanup; } json_object *json = ipc_json_describe_bar_config(bar); const char *json_string = json_object_to_json_string(json); - client_valid = - ipc_send_reply(client, json_string, - (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free } goto exit_cleanup; @@ -830,8 +841,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(modes, json_object_new_string(mode->name)); } const char *json_string = json_object_to_json_string(modes); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(modes); // free goto exit_cleanup; } @@ -841,34 +852,32 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *json = json_object_new_object(); json_object_object_add(json, "config", json_object_new_string(config->current_config)); const char *json_string = json_object_to_json_string(json); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free goto exit_cleanup; - } + } case IPC_SYNC: { // It was decided sway will not support this, just return success:false const char msg[] = "{\"success\": false}"; - ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); goto exit_cleanup; } default: - sway_log(SWAY_INFO, "Unknown IPC command type %i", client->current_command); + sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type); goto exit_cleanup; } exit_cleanup: - if (client_valid) { - client->payload_length = 0; - } free(buf); return; } -bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) { +bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, + const char *payload, uint32_t payload_length) { assert(payload); char data[IPC_HEADER_SIZE]; @@ -876,7 +885,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay memcpy(data, ipc_magic, sizeof(ipc_magic)); memcpy(&data32[0], &payload_length, sizeof(payload_length)); - memcpy(&data32[1], &client->current_command, sizeof(client->current_command)); + memcpy(&data32[1], &payload_type, sizeof(payload_type)); while (client->write_buffer_len + IPC_HEADER_SIZE + payload_length >= client->write_buffer_size) { @@ -908,6 +917,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay ipc_client_handle_writable, client); } - sway_log(SWAY_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); + sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", + payload_type, client->fd, payload); return true; } diff --git a/sway/main.c b/sway/main.c index 6754190f9..0477bbc3f 100644 --- a/sway/main.c +++ b/sway/main.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -15,7 +14,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/debug.h" #include "sway/server.h" #include "sway/swaynag.h" #include "sway/tree/root.h" @@ -91,7 +89,7 @@ void detect_proprietary(int allow_unsupported_gpu) { char *line = NULL; size_t line_size = 0; while (getline(&line, &line_size, f) != -1) { - if (strstr(line, "nvidia")) { + if (strncmp(line, "nvidia ", 7) == 0) { if (allow_unsupported_gpu) { sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); @@ -186,11 +184,7 @@ static void log_kernel(void) { static bool drop_permissions(void) { if (getuid() != geteuid() || getgid() != getegid()) { - if (setgid(getgid()) != 0) { - sway_log(SWAY_ERROR, "Unable to drop root, refusing to start"); - return false; - } - if (setuid(getuid()) != 0) { + if (setuid(getuid()) != 0 || setgid(getgid()) != 0) { sway_log(SWAY_ERROR, "Unable to drop root, refusing to start"); return false; } @@ -210,8 +204,6 @@ void enable_debug_flag(const char *flag) { debug.damage = DAMAGE_RERENDER; } else if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; - } else if (strcmp(flag, "render-tree") == 0) { - debug.render_tree = true; } else if (strcmp(flag, "txn-wait") == 0) { debug.txn_wait = true; } else if (strcmp(flag, "txn-timings") == 0) { @@ -398,7 +390,7 @@ int main(int argc, char **argv) { load_swaybars(); run_deferred_commands(); - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } diff --git a/sway/meson.build b/sway/meson.build index 293a4ed20..05cece7a4 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -2,7 +2,6 @@ sway_sources = files( 'commands.c', 'config.c', 'criteria.c', - 'debug-tree.c', 'decoration.c', 'ipc-json.c', 'ipc-server.c', @@ -23,6 +22,7 @@ sway_sources = files( 'input/input-manager.c', 'input/seat.c', + 'input/seatop_default.c', 'input/seatop_down.c', 'input/seatop_move_floating.c', 'input/seatop_move_tiling.c', @@ -30,6 +30,7 @@ sway_sources = files( 'input/seatop_resize_tiling.c', 'input/cursor.c', 'input/keyboard.c', + 'input/switch.c', 'config/bar.c', 'config/output.c', @@ -62,6 +63,7 @@ sway_sources = files( 'commands/fullscreen.c', 'commands/gaps.c', 'commands/hide_edge_borders.c', + 'commands/inhibit_idle.c', 'commands/kill.c', 'commands/mark.c', 'commands/opacity.c', @@ -71,6 +73,8 @@ sway_sources = files( 'commands/mode.c', 'commands/mouse_warping.c', 'commands/move.c', + 'commands/new_float.c', + 'commands/new_window.c', 'commands/no_focus.c', 'commands/nop.c', 'commands/output.c', @@ -168,6 +172,8 @@ sway_sources = files( 'commands/output/mode.c', 'commands/output/position.c', 'commands/output/scale.c', + 'commands/output/subpixel.c', + 'commands/output/toggle.c', 'commands/output/transform.c', 'tree/arrange.c', @@ -181,7 +187,6 @@ sway_sources = files( sway_deps = [ cairo, - gdk_pixbuf, jsonc, libevdev, libinput, diff --git a/sway/server.c b/sway/server.c index 0fcd58003..a403d8b3c 100644 --- a/sway/server.c +++ b/sway/server.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -62,7 +61,6 @@ bool server_init(struct sway_server *server) { server->data_device_manager = wlr_data_device_manager_create(server->wl_display); - wlr_gamma_control_manager_create(server->wl_display); wlr_gamma_control_manager_v1_create(server->wl_display); wlr_gtk_primary_selection_device_manager_create(server->wl_display); @@ -120,6 +118,15 @@ bool server_init(struct sway_server *server) { server->presentation = wlr_presentation_create(server->wl_display, server->backend); + server->output_manager_v1 = + wlr_output_manager_v1_create(server->wl_display); + server->output_manager_apply.notify = handle_output_manager_apply; + wl_signal_add(&server->output_manager_v1->events.apply, + &server->output_manager_apply); + server->output_manager_test.notify = handle_output_manager_test; + wl_signal_add(&server->output_manager_v1->events.test, + &server->output_manager_test); + wlr_export_dmabuf_manager_v1_create(server->wl_display); wlr_screencopy_manager_v1_create(server->wl_display); wlr_data_control_manager_v1_create(server->wl_display); @@ -184,6 +191,8 @@ bool server_start(struct sway_server *server) { &server->xwayland_ready); server->xwayland_ready.notify = handle_xwayland_ready; + setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true); + server->xwayland.xcursor_manager = wlr_xcursor_manager_create(cursor_theme, cursor_size); wlr_xcursor_manager_load(server->xwayland.xcursor_manager, 1); diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 13827e5ee..d53574aae 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -13,9 +13,8 @@ Sway allows configuring swaybar in the sway configuration file. *status_command* Executes the bar _status command_ with _sh -c_. Each line of text printed to stdout from this command will be displayed in the status area of the - bar. You may also use the i3bar JSON protocol: - - https://i3wm.org/docs/i3bar-protocol.html + bar. You may also use swaybar's JSON status line protocol. See + *swaybar-protocol*(7) for more information on the protocol If running this command via IPC, you can disable a running status command by setting the command to a single dash: _swaybar bar bar-0 status\_command -_ @@ -84,11 +83,13 @@ Sway allows configuring swaybar in the sway configuration file. debug-events`. To disable the default behavior for a button, use the command _nop_. -*mode* dock|hide|invisible +*mode* dock|hide|invisible|overlay Specifies the visibility of the bar. In _dock_ mode, it is permanently visible at one edge of the screen. In _hide_ mode, it is hidden unless the modifier key is pressed, though this behaviour depends on the hidden state. - In _invisible_ mode, it is permanently hidden. Default is _dock_. + In _invisible_ mode, it is permanently hidden. In _overlay_ mode, it is + permanently visible on top of other windows. (In _overlay_ mode the bar is + transparent to input events.) Default is _dock_. *hidden_state* hide|show Specifies the behaviour of the bar when it is in _hide_ mode. When the @@ -110,6 +111,12 @@ Sway allows configuring swaybar in the sway configuration file. the bar. This value will be multiplied by the output scale. The default is _3_. +*unbindcode* [--release] + Removes the binding with the given . + +*unbindsym* [--release] button[1-9]| + Removes the binding with the given