diff --git a/.builds/archlinux.yml b/.build.yml similarity index 54% rename from .builds/archlinux.yml rename to .build.yml index 2c8ffcf45..144333118 100644 --- a/.builds/archlinux.yml +++ b/.build.yml @@ -1,37 +1,30 @@ +# vim: ft=yaml ts=2 sw=2 et : image: archlinux packages: - - cairo - - gdk-pixbuf2 - - json-c - - lcms2 - - libdisplay-info - - libegl - - libinput - - libxcb - - libxkbcommon - meson + - xorg-server-xwayland + - xcb-util-image + - json-c - pango - - pcre2 - - scdoc + - cairo - wayland - wayland-protocols - - xcb-util-image - - xcb-util-wm - - xorg-xwayland - - seatd - - hwdata + - gdk-pixbuf2 + - libinput + - libxkbcommon + - scdoc sources: - https://github.com/swaywm/sway - - https://gitlab.freedesktop.org/wlroots/wlroots.git + - https://github.com/swaywm/wlroots tasks: - wlroots: | cd wlroots - meson setup --prefix=/usr build -Dexamples=false + meson --prefix=/usr build ninja -C build sudo ninja -C build install - setup: | cd sway - meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd + meson build - build: | cd sway ninja -C build diff --git a/.builds/alpine.yml b/.builds/alpine.yml deleted file mode 100644 index fa693906d..000000000 --- a/.builds/alpine.yml +++ /dev/null @@ -1,56 +0,0 @@ -image: alpine/edge -packages: - - cairo-dev - - eudev-dev - - gdk-pixbuf-dev - - json-c-dev - - lcms2-dev - - libdisplay-info-dev - - libevdev-dev - - libinput-dev - - libseat-dev - - libxcb-dev - - libxkbcommon-dev - - mesa-dev - - meson - - pango-dev - - pcre2-dev - - pixman-dev - - scdoc - - wayland-dev - - wayland-protocols - - xcb-util-image-dev - - xcb-util-wm-dev - - xwayland-dev - - hwdata-dev -sources: - - https://github.com/swaywm/sway - - https://gitlab.freedesktop.org/wlroots/wlroots.git -tasks: - - wlroots: | - cd wlroots - meson setup --prefix=/usr build -Dexamples=false - ninja -C build - sudo ninja -C build install - - setup: | - cd sway - meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled - - build: | - cd sway - ninja -C build - - build-no-xwayland: | - cd wlroots - meson configure build -Dxwayland=disabled - ninja -C build - sudo ninja -C build install - - cd ../sway - meson configure build --clearcache - ninja -C build - - build-static: | - cd sway - mkdir subprojects - ln -s ../../wlroots subprojects/wlroots - rm -rf build - meson setup build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots - ninja -C build diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml deleted file mode 100644 index a3df06e6d..000000000 --- a/.builds/freebsd.yml +++ /dev/null @@ -1,45 +0,0 @@ -image: freebsd/latest -packages: -- devel/basu -- devel/json-c -- devel/libevdev -- devel/meson -- devel/pcre2 -- devel/pkgconf -- graphics/cairo -- graphics/gdk-pixbuf2 -- graphics/lcms2 -- graphics/wayland -- graphics/wayland-protocols -- textproc/scdoc -- x11-toolkits/pango -- x11/libxcb -- x11/libxkbcommon -# wlroots dependencies -- devel/evdev-proto -- devel/libepoll-shim -- devel/libudev-devd -- graphics/libdrm -- graphics/mesa-libs -- sysutils/libdisplay-info -- sysutils/seatd -- x11/libinput -- x11/libX11 -- x11/pixman -- x11/xcb-util-wm -- x11-servers/xwayland -- misc/hwdata -sources: -- https://github.com/swaywm/sway -- https://gitlab.freedesktop.org/wlroots/wlroots.git -tasks: -- setup: | - cd sway - mkdir subprojects - cd subprojects - ln -s ../../wlroots wlroots - cd .. - meson setup build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu -- build: | - cd sway - ninja -C build diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..5818da3ca --- /dev/null +++ b/.clang-format @@ -0,0 +1,16 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +TabWidth: 4 +UseTab: Always +BreakBeforeBraces: Attach +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +SortIncludes: false +ColumnLimit: 80 +AlignAfterOpenBracket: DontAlign +BinPackParameters: false +BinPackArguments: false +ContinuationIndentWidth: 8 +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortLoopsOnASingleLine: true +ReflowComments: false diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index eba606e65..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Bugs -about: Crashes and other bugs -labels: 'bug' - ---- - -### Please read the following before submitting: -- Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on Libera Chat. -- Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway. -- Please do NOT submit issues for information from the github wiki. The github wiki is community maintained and therefore may contain outdated information, scripts that don't work or obsolete workarounds. - If you fix a script or find outdated information, don't hesitate to adjust the wiki page. - -### Please fill out the following: -- **Sway Version:** - - `swaymsg -t get_version` or `sway -v` - -- **Debug Log:** - - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. - - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. - - Attach the **full** file, do not truncate it. - -- **Configuration File:** - - Please try to produce with the default configuration. - - If you cannot reproduce with the default configuration, please try to find the minimal configuration to reproduce. - - Upload the config to a pastebin such as gist.github.com. - -- **Stack Trace:** - - This is only needed if sway crashes. - - If you use systemd, you should be able to open the coredump of the most recent crash with gdb with - `coredumpctl gdb sway` and then `bt full` to obtain the stack trace. - - If the lines mentioning sway or wlroots have `??` for the location, your binaries were built without debug symbols. Please compile both sway and wlroots from source and try to reproduce. - -- **Description:** - - The steps you took in plain English to reproduce the problem. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 0092609b5..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Questions - url: "https://libera.chat" - about: "Please ask questions on IRC in #sway on Libera Chat" diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md deleted file mode 100644 index 9be069a7a..000000000 --- a/.github/ISSUE_TEMPLATE/enhancement.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: Enhancements -about: New functionality -labels: 'enhancement' - ---- - -### Please read the following before submitting: -- We are not accepting any new window management features unless they get implemented by i3. - -### Please fill out the following: -- **Description:** -Please describe in plain English what the enhancement is and what the use case is. diff --git a/.github/ISSUE_TEMPLATE/i3_compat.md b/.github/ISSUE_TEMPLATE/i3_compat.md deleted file mode 100644 index 4dfa21c3c..000000000 --- a/.github/ISSUE_TEMPLATE/i3_compat.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: i3 Compatibility -about: Sway behaves differently from or lacks i3 functionality -labels: 'i3-compat' - ---- - -### Please read the following before submitting: -- The following either do not make sense for Wayland or we have decided against supporting: - - `restart` - - `resource` - - saving and loading layouts - - the i3 sync protocol - -### Please fill out the following: -- **i3 PR:** - - If this is new i3 functionality, please add a link to the i3 pull request. - -- **Description:** - - Please describe in plain English how Sway and i3's behaviors differ or what functionality Sway is lacking. diff --git a/.gitignore b/.gitignore index 167960e2e..1ec0cb523 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ bin/ test/ build/ build-*/ -.cache/ .lvimrc config-debug wayland-*-protocol.* diff --git a/.mailmap b/.mailmap deleted file mode 100644 index 35ee25b0c..000000000 --- a/.mailmap +++ /dev/null @@ -1 +0,0 @@ -Ronan Pigott diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f043f7a7..031f51f53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,13 @@ # Contributing to sway Contributing just involves sending a pull request. You will probably be more -successful with your contribution if you visit #sway-devel on Libera Chat -upfront and discuss your plans. +successful with your contribution if you visit +[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on +irc.freenode.net upfront and discuss your plans. Note: rules are made to be broken. Adjust or ignore any/all of these as you see fit, but be prepared to justify it to your peers. -## Scope of future changes to sway - -**Important**: Sway has completed its core value proposition: it is a fully -featured Wayland-compatible replacement for i3. It is not our intention to -expand on the scope of what i3 aims to accomplish. Our priorities now are -increasing the stability, reliability, and performance of sway within its -current scope. For this reason, most new window management feature requests are -not accepted, even if accompanied by a patch. - ## Pull Requests If you already have your own pull request habits, feel free to use them. If you diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..7caa5d1e6 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,42 @@ +If you are using the nvidia proprietary driver for any reason, you have two choices: + +1. Uninstall it and use nouveau instead +2. Use X11+i3 and close your browser tab + +If `lsmod | grep nvidia | wc -l` shows anything other than zero, your bug report is not welcome here. + +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: + + swaymsg -t get_version + +If this doesn't work, use: + + sway -v + +* Sway Version: + +Obtain a debug log like so: + + sway -d 2> ~/sway.log + +This will record information about sway's activity when it's running. Briefly reproduce your problem and exit sway. When preparing a debug log, brevity is important - start up sway, do the minimum work necessary to reproduce the error, then close sway. + +Upload the debug log to a pastebin service such as [gist.github.com](https://gist.github.com), and link to it below. + +* Debug Log: + +You should try to reproduce the issue with the default configuration. If you cannot, please reproduce with a minimal configuration, upload the config to a pastebin service, and link to it below. + +* Configuration File: + +Finally, explain the steps you took in plain English to reproduce the problem below. + +* Stack Trace, if sway crashes: + +If you use systemd, you should be able to open the coredump of the most recent crash with GDB like so: + + coredumpctl gdb sway + +And then type `bt full` to obtain the stack trace. diff --git a/README.ar.md b/README.ar.md deleted file mode 100644 index 2f64f2b53..000000000 --- a/README.ar.md +++ /dev/null @@ -1,91 +0,0 @@ -# sway - -sway - -هو مدير للمجموعات المركبة لـ[Wayland] متوافق مع [i3] - - -إقرأ [الأسئلة الشائعة](https://github.com/swaywm/sway/wiki) - - انضم الى [قناة IRC](https://web.libera.chat/gamja/?channels=#sway) - - -## تواقيع الإصدار - تٌوقع الإصدارات بـواسطة [E88F5E48] و تُنشر على [GitHub](https://github.com/swaywm/sway/releases) - -## التثبيت - -### بإستخدام الحزم - -يتوفر Sway للعديد من التوزيعات، حاول تثبيت حزمة "sway" لتوزيعتك -### التجميع من المصدر -إطلع على [صفحة الويكي هذه](https://github.com/swaywm/sway/wiki/Development-Setup) إذا أردت بناء الـHEAD من sway و wlroots لأغراض الفحص والتطوير - -تثبيت اللوازم: -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (optional: system tray) -* [scdoc] (optional: man pages) \* -* git (optional: version info) \* - -_\* Compile-time dep_ - -نفذ هذه الأوامر: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## الإعدادات - -إذا كنت بالفعل تستخدم i3، فعليك نسخ إعدادات i3 لديك إلى `~/.config/sway/config` وسوف تعمل تلقائياً. - -و إلا عليك نسخ ملف الإعدادات النموذج إلى `config/sway/config` الموجود عادةً في `/etc/sway/config.` - - -## التشغيل - -شغل `sway` بإستخدام أمر TTY. -قد يعمل بعض مدراء العرض مع أنهم غير مدعومون من sway -(gdm مثلاً يعمل بشكل جيد إلى حد ما) - -[en]: https://github.com/swaywm/sway#readme -[ar]: README.ar.md -[cs]: README.cs.md -[de]: README.de.md -[dk]: README.dk.md -[es]: README.es.md -[fr]: README.fr.md -[ge]: README.ge.md -[gr]: README.gr.md -[hi]: README.hi.md -[hu]: README.hu.md -[ir]: README.ir.md -[it]: README.it.md -[ja]: README.ja.md -[ko]: README.ko.md -[nl]: README.nl.md -[no]: README.no.md -[pl]: README.pl.md -[pt]: README.pt.md -[ro]: README.ro.md -[ru]: README.ru.md -[sv]: README.sv.md -[tr]: README.tr.md -[uk]: README.uk.md -[zh-CN]: README.zh-CN.md -[zh-TW]: README.zh-TW.md -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.az.md b/README.az.md deleted file mode 100644 index 50c8b0dec..000000000 --- a/README.az.md +++ /dev/null @@ -1,66 +0,0 @@ -# sway - -sway [i3]-ə uyğun [Wayland] kompozitorudur. [Tez-tez verilən sualları] oxuyun. -[IRC kanalına] qoşulun ("irc.libera.chat"-da #sway). - -## Buraxılış İmzaları - -Buraxılışlar [E88F5E48] ilə imzalanıb və [GitHub-da][GitHub releases] dərc edilib. - -## Quraşdırma - -### Paketlərdən - -Sway bir çox distributivdə mövcuddur. Öz distributiviniz üçün -"sway" paketini quraşdırmağa çalışın. - -### Mənbə kodundan kompilyasiya - -Test və ya inkişaf üçün sway və wlroots-un HEAD-ini qurmaq istəyirsinizsə, -[bu viki səhifəsini][Development setup] nəzərdən keçirin. - -Asılılıqları quraşdırın: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (ixtiyari: sistem trayı üçün əlavə şəkil formatları) -* [swaybg] (ixtiyari: divar kağızı) -* [scdoc] (ixtiyari: man səhifələri) \* -* git (ixtiyari: versiya məlumatı) \* - -_\* Kompilyasiya asılılıqları_ - -Bu əmrləri icra edin: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## Konfiqurasiya - -Əgər artıq i3-dən istifadə edirsinizsə, i3 konfiqurasiyanızı `~/.config/sway/config` -ünvanına köçürün və o, dərhal işləyəcək. Əks halda, nümunə konfiqurasiya faylını -`~/.config/sway/config` ünvanına köçürün. O, adətən `/etc/sway/config` ünvanında yerləşir. -Konfiqurasiya haqqında məlumat üçün `man 5 sway` əmrini icra edin. - -## İşə Salma - -TTY-dan `sway`-ı işə salın. Bəzi ekran menecerləri işləyə bilər, lakin sway tərəfindən -dəstəklənmir (gdm-in kifayət qədər yaxşı işlədiyi məlumdur). - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[Tez-tez verilən sualları]: https://github.com/swaywm/sway/wiki -[IRC kanalına]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[swaybg]: https://github.com/swaywm/swaybg/ -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.bg.md b/README.bg.md new file mode 100644 index 000000000..dcdefc7e3 --- /dev/null +++ b/README.bg.md @@ -0,0 +1,78 @@ +# 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.cs.md b/README.cs.md deleted file mode 100644 index 0b4085e7f..000000000 --- a/README.cs.md +++ /dev/null @@ -1,92 +0,0 @@ -# sway - -sway je [waylandový][Wayland] kompozitor kompatibilní s [i3]. Přečtěte si -[FAQ (anglicky)][FAQ]. Připojte se na [IRC kanál (anglicky)][IRC channel] -\(#sway na irc.libera.chat). - -## Podpisy vydání - -Vydané verze jsou podepsány klíčem [E88F5E48] a publikovány -[na GitHubu][GitHub releases]. - -## Instalace - -### Z balíků - -Sway je dostupný v mnoha distribucích. Zkuste v té vaší nainstalovat balík "sway". - -### Kompilace ze zdrojových kódů - -Pokud chcete sestavit HEAD repozitáře sway a wlroots pro testování nebo vývoj, -použijte návod na [této stránce na wiki (anglicky)][Development setup]. - -Nainstalujte závislosti: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (volitelné: dodatečné formáty ikon pro oznamovací oblast) -* [swaybg] (volitelné: tapeta plochy) -* [scdoc] (volitelné: man stránky) \* -* git (volitelné: informace o verzi) \* - -_\* Závislost pouze pro kompilaci_ - -Spusťte tyto příkazy: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## Konfigurace - -Pokud již používáte i3, zkopírujte svou konfiguraci i3 do `~/.config/sway/config` -a ta bude ihned fungovat. Jinak zkopírujte do `~/.config/sway/config` ukázkový -konfigurační soubor. Ten se obvykle nachází v `/etc/sway/config`. -Pro více informací o konfiguraci spusťte `man 5 sway`. - -## Spuštění - -Spusťte `sway` z TTY nebo ze správce displeje. - -[en]: https://github.com/swaywm/sway#readme -[ar]: README.ar.md -[cs]: README.cs.md -[de]: README.de.md -[dk]: README.dk.md -[es]: README.es.md -[fr]: README.fr.md -[ge]: README.ge.md -[gr]: README.gr.md -[hi]: README.hi.md -[hu]: README.hu.md -[ir]: README.ir.md -[it]: README.it.md -[ja]: README.ja.md -[ko]: README.ko.md -[nl]: README.nl.md -[no]: README.no.md -[pl]: README.pl.md -[pt]: README.pt.md -[ro]: README.ro.md -[ru]: README.ru.md -[sv]: README.sv.md -[tr]: README.tr.md -[uk]: README.uk.md -[zh-CN]: README.zh-CN.md -[zh-TW]: README.zh-TW.md -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[swaybg]: https://github.com/swaywm/swaybg/ -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.de.md b/README.de.md index 0c0b18637..e8198013a 100644 --- a/README.de.md +++ b/README.de.md @@ -1,56 +1,84 @@ -# Sway -Sway ist ein [i3]-kompatibler [Wayland]-Compositor. Lies die [FAQ]. Tritt dem [IRC Channel] bei (#sway on irc.libera.chat; Englisch). +# sway -## Signaturen -Jeder Release wird mit dem PGP-Schlüssel [E88F5E48] signiert und [auf GitHub][GitHub releases] veröffentlicht. +Der Fortschritt dieser Übersetzung kann [hier](https://github.com/swaywm/sway/issues/1318) +eingesehen werden. + +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. ## Installation -### Über die Paketverwaltung +### Als Paket -Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Versuche einfach das Paket "sway" zu installieren. +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 -* meson \* -* [wlroots] +Abhängigkeiten: + +* meson +* [wlc](https://github.com/Cloudef/wlc) * wayland -* wayland-protocols\* -* pcre2 -* json-c +* xwayland +* libinput >= 1.6.0 +* libcap +* pcre +* json-c >= 0.13 * pango * cairo -* gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) -* [swaybg] (Optional, wird für das Setzen von Desktophintergrundbildern benötigt) -* [scdoc] (Optional, wird für die Dokumentation (Man Pages) benötigt)\* -* git (Optional: Versionsinfo)\* +* gdk-pixbuf2 * +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (erforderlich für man pages) +* git -_\*Werden nur für das Kompilieren benötigt_ +_\*Nur erforderlich für swaybar, swaybg_ -Führe die folgenden Befehle aus: +Führe diese Befehle aus: - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install + meson build + ninja -C build + sudo ninja -C build install -Schaue in das [Wiki][Development setup] (Englisch) für Informationen, falls du zum Testen oder Entwickeln den neuesten Stand (HEAD) von sway und wlroots kompilieren willst. +In Systemen ohne logind musst du `sway` das suid-Flag geben: + + sudo chmod a+s /usr/local/bin/sway ## Konfiguration -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`. +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. -## Sway starten -Sway kann einfach mit dem Befehl `sway` vom TTY oder mithilfe eines Displaymanagers gestartet werden. +## Verwendung -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[swaybg]: https://github.com/swaywm/swaybg -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc +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). diff --git a/README.dk.md b/README.dk.md deleted file mode 100644 index 8e4cc3724..000000000 --- a/README.dk.md +++ /dev/null @@ -1,67 +0,0 @@ -# Sway - -Sway er en [i3]-kompatibel [Wayland] compositor. Læs [Ofte stillede spørgsmål]. -Deltag på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat). - -## Udgivelses Signaturer - -Udgivelser er signeret med [E88F5E48] og publiceret [på GitHub][GitHub -releases]. - -## Installation - -### Fra pakker - -Sway er tilgængelig i mange distributioner. Prøv at installere "sway" pakken -fra din. - -Hvis du er interesseret i at pakke Sway til din distribution, kan du tage forbi -IRC kanalen eller sende en email til sir@cmpwn.com for rådgivning. - -### Kompilering fra kildekode - -Se [denne wiki-side][Opsætning til udvikling] hvis du vil bygge HEAD af sway og -wlroots til test eller udvikling. - -Installationsafhængigheder: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (valgfrit: system tray) -* [scdoc] (valgfrit: man pages) \* -* git \* - -_\*Kompileringsafhængighed_ - -Kør følgende kommandoer: - - meson setup build - ninja -C build - sudo ninja -C build install - -## Konfiguration - -Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til -`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurationsfilen til -`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. Kør -`man 5 sway` for at få oplysninger om konfigurationen. - -## Eksekvering - -Kør `sway` fra en TTY eller fra en display manager. - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[Ofte stillede spørgsmål]: https://github.com/swaywm/sway/wiki -[IRC kanal]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.el.md b/README.el.md new file mode 100644 index 000000000..bc2360ca7 --- /dev/null +++ b/README.el.md @@ -0,0 +1,73 @@ +# 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 deleted file mode 100644 index 3ad8e29e3..000000000 --- a/README.es.md +++ /dev/null @@ -1,57 +0,0 @@ -# sway - -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](https://web.libera.chat/gamja/?channels=#sway) (#sway on -irc.libera.chat). - -## Firmas de las versiones - -Las distintas versiones están firmadas con [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) -y publicadas [en GitHub](https://github.com/swaywm/sway/releases). - -## Instalación - -### Usando paquetes - -Sway está disponible en muchas distribuciones. Pruebe instalando el paquete "sway" desde la suya. -Si no está disponible, puede consultar [esta documentación](https://github.com/swaywm/sway/wiki/Unsupported-packages) -y así obtener información acerca de como instalarlo. - -Si está interesado en crear un paquete para su distribución, únase al canal de IRC o -escriba un email a sir@cmpwn.com - -### Compilando el código fuente - -Instale las dependencias: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (optional: system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* -* git \* - -_\*Compile-time dep_ - -Desde su consola, ejecute las órdenes: - - meson setup build - ninja -C build - sudo ninja -C build install - -## Configuración - -Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y -sway funcionará sin tener que configurar nada más. En otro caso, copie el archivo de -configuración básico a `~/.config/sway/config`, normalmente se encuentra en `/etc/sway/config`. -Ejecute `man 5 sway` para obtener información sobre la configuración. - -## Ejecución - -Ejecute `sway` desde su consola. Algunos gestores de pantalla pueden funcionar sin estar -soportados por `sway` (sabemos que gdm funciona bastante bien). diff --git a/README.fr.md b/README.fr.md index c4ef699a4..ad627ba54 100644 --- a/README.fr.md +++ b/README.fr.md @@ -1,75 +1,82 @@ # sway -Sway est un compositeur [Wayland] compatible avec [i3]. Lisez la -[FAQ]. Rejoignez le [canal IRC] (#sway sur irc.libera.chat). +Sway est un compositeur [Wayland](http://wayland.freedesktop.org/) compatible +avec i3, **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. ## Aide en français -[abdelq] fournit du support en français sur IRC et Github, dans le fuseau -horaire UTC-4 (EST). +[abdelq](//github.com/abdelq) fournit du support en français sur IRC et Github, dans le fuseau horaire UTC-4 (EST). ## Signatures de nouvelles versions -Les nouvelles versions sont signées avec [E88F5E48] et publiées -[sur GitHub][versions GitHub]. +Les nouvelles versions sont signées avec [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) +et publiées [sur GitHub](https://github.com/swaywm/sway/releases). ## Installation ### À partir de paquets -Sway est disponible sur beaucoup de distributions. Essayez d'installer le -paquet "sway" pour la vôtre. +Sway est disponible sur plusieurs distributions. Essayez d'installer le paquet "sway" pour +la vôtre. Si ce n'est pas disponible, consultez [cette page wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) +pour de l'information sur l'installation pour vos distributions. -Si vous êtes intéressé à maintenir Sway pour votre distribution, passez sur le -canal IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des -conseils. +Si vous êtes intéressé à maintenir Sway pour votre distribution, passez par le canal +IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des conseils. -### Compilation depuis les sources - -Consultez [cette page wiki][Configuration de développement] si vous souhaitez -compiler la révision HEAD de sway et wlroots pour tester ou développer. +### Compilation depuis la source Installez les dépendances : -* meson \* -* [wlroots] +* meson +* [wlc](https://github.com/Cloudef/wlc) * wayland -* wayland-protocols \* -* pcre2 -* json-c +* xwayland +* libinput >= 1.6.0 +* libcap +* pcre +* json-c >= 0.13 * pango * cairo -* gdk-pixbuf2 (optionnel : system tray) -* [scdoc] (optionnel : requis pour les pages man) \* -* git (optionnel : information de version) \* +* gdk-pixbuf2 * +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (requis pour les pages man) +* git -_\* Requis uniquement pour la compilation_ +_\*Uniquement requis pour swaybar, swaybg_ Exécutez ces commandes : - meson setup build + meson build ninja -C build sudo ninja -C build install +Sur les systèmes sans logind, vous devez suid le binaire de sway : + + sudo chmod a+s /usr/local/bin/sway + ## Configuration -Si vous utilisez déjà i3, copiez votre configuration i3 vers -`~/.config/sway/config` et sway fonctionnera directement. Sinon, copiez -l'exemple de fichier de configuration vers `~/.config/sway/config`. Il se -trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour lire la -documentation pour la configuration de sway. +Si vous utilisez déjà i3, copiez votre configuration i3 à `~/.config/sway/config` et +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 ou d'un gestionnaires d'affichage. - -[Wayland]: http://wayland.freedesktop.org/ -[i3]: https://i3wm.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[canal IRC]: https://web.libera.chat/gamja/?channels=#sway -[abdelq]: https://github.com/abdelq -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[versions GitHub]: https://github.com/swaywm/sway/releases -[Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc +Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent fonctionner, +mais ne sont pas supportés par Sway (gdm est réputé pour assez bien fonctionner). diff --git a/README.ge.md b/README.ge.md deleted file mode 100644 index de75be837..000000000 --- a/README.ge.md +++ /dev/null @@ -1,61 +0,0 @@ -# sway - -sway არის [i3]-თავსებადი [Wayland]-ის კომპოზიტორი. მეტი ინფორმაციისთვის იხილეთ -[FAQ]. დაუკავშირდით [IRC არხს][IRC channel] \(#sway irc.libera.chat-ზე). - -## გამოშვების ხელმოწერები - -გამოშვებები ხელმოწერილია [E88F5E48]-ით და გამოქვეყნებულია [GitHub-ზე][GitHub releases]. - -## ინსტალაცია - -### რეპოზიტორიიდან - -Sway არის ხელმისაწვდომი ბევრი დისტრიბუტაციისთვის. ცადეთ "sway" პაკეტის ინსტალაცია თქვენთვის. - -### კოდის კომპილაცია - -იხილეთ [ეს ვიკი გვერდი][Development setup] თუ გინდათ რომ ააწყოთ sway და wlroots სატესტოდ ან დეველოპმენტისთვის. - -დააინსტალირეთ დამოკიდებულებები: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (ასევე არჩევითია: system tray) -* [scdoc] (ასევე არჩევითია: man pages) \* -* git (ასევე არჩევითია: version info) \* - -_\* Compile-time dep_ - -გაუშვით ეს ბრძანებები: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## კონფიგურაცია - -თუ უკვე იყენებთ i3-ს, მაშინ დააკოპირე i3 კონფიგურაცია და ჩასვი `~/.config/sway/config` -და უპრობლემოდ იმუშავებს პირდაპირ. წინააღმდეგ შემთხვევაში კონფიგურაციის ნიმუში ჩააკოპირეთ აქ: `~/.config/sway/config`. კომპიგურაციის ნიმუში ხშირ შემთხვევაში არის `/etc/sway/config`. -გაუშვი `man 5 sway` კონპიგურაციაზე ინფორმაციის მისაღებად. - -## გაშვება - -გაუშვი `sway` TTY-ისთვის. ზოგიერთმა ლოგინ მენეჯერმა შეიძლება იმუშავოს, მაგრამ არ -არის მხარდაჭერილი sway-სგან (როგორც წესი კარგად მუშაობს gdm). - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.gr.md b/README.gr.md deleted file mode 100644 index 629465846..000000000 --- a/README.gr.md +++ /dev/null @@ -1,67 +0,0 @@ -# Sway - -Το sway ένα [i3]-συμβατό [Wayland] compositor. Διαβάστε το [FAQ]. Μπείτε στο -[IRC channel] \(#sway on irc.libera.chat). - -## Υπογραφές δημοσιεύσεων - -Οι εκδόσεις είναι υπογεραμμένες με [E88F5E48] και δημοσιευμένες [στο GitHub][GitHub releases]. - -## Εγκατάσταση - -### Από πακέτα - -Το Sway είναι διαθέσιμο σε πολλά distributions. Δοκιμάστε εγκαταστώντας το "sway" package για -το δικό σας. - -Εάν ενδιαφέρεστε για packaging του sway για το distribution σας, να πάτε στο IRC -channel ή στείλτε ένα email στο sir@cmpwn.com για συμβουλές. - -### Compiling από πηγή - -Τσεκάρετε [αυτό το wiki page][Development setup] εάμα θέλετε να κάνετε build το HEAD του -sway και wlroots γιά τεστάρισμα ή development. - -Εγκατάσταση των dependencies: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (προαιρετικό: system tray) -* [scdoc] (προαιρετικό: man pages) \* -* git (προαιρετικό: πληροφορίες εκδώσεων) \* - -_\*Compile-time dep_ - -Τρέξτε αυτά τα commands: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## Configuration - -Εάν ήδη χρησιμοποιήτε το i3, αντιγράψτε το i3 config σας στο `~/.config/sway/config` και -θα δουλέψει out of the box. Αλλιώς, αντιγράψτε το sample configuration αρχείο στο -`~/.config/sway/config`. Το οποίο συνήθως βρίσκεται στο `/etc/sway/config`. -Κάντε run `man 5 sway` για πληροφορίες τού configuration. - -## Τρέχοντας - -Τρέξτε `sway` από ένα TTY. Μερίκα display managers μπορεί να δουλέψουν αλλά δέν είναι συμβατά με -το sway (το gdm γνωρίζεται να δουλέβει σχετικά καλά). - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc \ No newline at end of file diff --git a/README.hi.md b/README.hi.md deleted file mode 100644 index eb792b790..000000000 --- a/README.hi.md +++ /dev/null @@ -1,63 +0,0 @@ -# sway - -sway एक [i3](https://i3wm.org/)-अनुकूल -[Wayland](https://wayland.freedesktop.org/) Compositor है। -[FAQ](https://github.com/swaywm/sway/wiki) पढिये। [IRC -Channel](https://web.libera.chat/gamja/?channels=#sway) -(irc.libera.chat पर #sway) में भी जुडिये। - -## रिलीज हस्ताक्षर - -रिलीजें -[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) -से साइन होतें हैं और [Github पर](https://github.com/swaywm/sway/releases) प्रकाशित होते हैं। - -## इंस्टौलेशन - -### पैकेजों के द्वारा - -Sway कई distributions में उप्लब्ध है। आप अपने में "sway" नामक पैकेज इंस्टौल करके देख -सकते हैं। - -### Source से compile करके - -यदि आप परीक्षण और विकास के लिए sway और wlroots के नवीनतम संस्करण बनाना -चाहते हैं, तो [यह विकी -पृष्ठ](https://github.com/swaywm/sway/wiki/Development-Setup) देखें। - -निर्भरताएं: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf (वैकल्पिक: system tray के लिये) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (वैकल्पिक: man पृष्ठों के लिये) - \* -* git (वैकल्पिक: संस्करण जानने के लिये) - -_\* Compilation के समय आवश्यक_ - -ये commands चलाएं: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## Configuration - -अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को -`~/.config/sway/config` में copy कर लीजिये और वह बिना किसी परिवर्तन के काम -करेगा। अन्यथा, नमूने configuration file को `~/.config/sway/config` में copy -कर लीजिये। यह सामान्यतः `/etc/sway/config` में पाया जाता है। `man 5 -sway` से आप configuration के बारे में जानकारी प्राप्त कर सकते हैं। - -## चलाना - -आप एक tty से `sway` को चला सकते हैं। कुछ display managers काम करते हैं परन्तु ये -sway के द्वारा समर्थित नहीं है (gdm के बारे में जाना गया है कि वह सही काम करता -है)। diff --git a/README.hu.md b/README.hu.md deleted file mode 100644 index b66a24b23..000000000 --- a/README.hu.md +++ /dev/null @@ -1,70 +0,0 @@ -# sway - -A Sway egy [i3]-kompatibilis [Wayland]-kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC-csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en). - -## Csomagaláírások - -A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva, és [GitHubon][GitHub releases] publikálva. - -## Telepítés - -### Csomagból - -A Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a "sway" -csomagot telepíteni az általad használt eszközzel. - -Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC- -csatornára, vagy küldj levelet a sir@cmpwn.com címre tanácsokért. - -### Fordítás forráskódból - -Olvasd el [ezt a wikioldalt][Development setup], ha szeretnéd tesztelési vagy -fejlesztési célokból lefordítani az aktuális (HEAD) állapotát a `sway`-nek és a -`wlroots`-nak. - -Telepítsd a függőségeket: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (opcionális: system tray) -* [scdoc] (opcionális: man pages) \* -* git (opcionális: version info) \* - -_\*Fordításidejű függőség_ - -Futtasd ezeket a parancsokat: - - meson setup build - ninja -C build - sudo ninja -C build install - -## Konfiguráció - -Ha előzőleg i3-at használtál, akkor átmásolhatod az i3-beállításaidat a -`~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold -le kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési -útvonalon található. -Futtasd a `man 5 sway` parancsot további információért a konfigurációval -kapcsolatban. - -## Futtatás - -Futtasd a `sway` parancsot egy TTY-felületről. Néhány bejelentkezéskezelő -(display manager) működhet, de alapvetően nem támogatottak a sway által. (A -gdm-ről ismeretes, hogy egész jól működik.) - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.ir.md b/README.ir.md deleted file mode 100644 index f7d00e803..000000000 --- a/README.ir.md +++ /dev/null @@ -1,54 +0,0 @@ -# sway - -‏sway یک کامپوزیتور الهام گرفته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال -IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (‎#sway‏ در -irc.libera.chat). - -برای حمایت از تیم توسعه sway به [صفحه -Patreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مراجعه کنید. - -## امضای نسخه‌ها - -امضای نسخه‌ها با [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 ایمیل بزنید. - -### کامپایل کردن کد - -چنانچه می‌خواهید آخرین نسخه کد sway و wlroots را برای آزمایش یا توسعه بسازید به این [صفحه راهنما](https://github.com/swaywm/sway/wiki/Development-Setup) مراجعه کنید. - -بسته‌های مورد نیاز: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (انتخابی: برای system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (انتخابی: برای صفحه‌های راهنما) \* -* git (انتخابی: برای اطلاع در خصوص نسخه‌ها) \* - -_\*نیازمندی‌های زمان کامپایل برنامه_ - -این فرمان‌ها را اجرا کنید: - - meson setup build - ninja -C build - sudo ninja -C build install - -### شخصی سازی و تنظیمات - -اگر در حال حاضر از i3 استفاده می‌کنید، تنظیمات i3 خودتان را در فایل ‪`~/.config/sway/config`‬ کپی کنید و بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، فایل نمونه تنظیمات را استفاده کنید. این فایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید. - -## اجرا - -در محیط TTY کافیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طرف sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد). diff --git a/README.it.md b/README.it.md index 30cdc21e0..bfe099205 100644 --- a/README.it.md +++ b/README.it.md @@ -1,65 +1,79 @@ # sway -sway è un compositore di [Wayland] compatibile con [i3]. Leggi le [FAQ]. -Unisciti al [canale IRC] \(#sway su irc.libera.chat). +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). -## Firma delle versioni +[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) -Le versioni sono firmate con la chiave [E88F5E48] e sono pubblicate -[su GitHub][GitHub releases]. +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 -### Da un pacchetto +### Dai pacchetti -Sway è disponibile in molte distribuzioni, prova a installare il pacchetto -"sway" per la tua. +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. -### Compilazione dei sorgenti +Se vuoi creare un pacchetto per la tua distribuzione, passa dall'IRC o manda un email (in Inglese) +a sir@cmpwn.com. -Consulta [questa pagina del wiki][Development setup] se vuoi compilare l'HEAD -di sway e wlroots per testarli o contribuire allo sviluppo. +### Compilando il codice sorgente -Installa le dipendenze: +Installa queste dipendenze: -* meson \* -* [wlroots] +* meson +* [wlc](https://github.com/Cloudef/wlc) * wayland -* wayland-protocols \* +* xwayland +* libinput >= 1.6.0 +* libcap * pcre -* json-c +* json-c >= 0.13 * pango * cairo -* gdk-pixbuf2 (opzionale: area di notifica) -* [scdoc] (opzionale: pagine del manuale) \* -* git (opzionale: informazioni sulla versione) \* +* gdk-pixbuf2 * +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (rrichiesto per man pages) +* git -_\* Dipendenza necessaria per la compilazione_ +_\*Richiesto solo per swaybar, swaybg_ Esegui questi comandi: - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install + 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 hai già usato i3, copia il tuo file di configurazione in -`~/.config/sway/config` e sway funzionerà immediatamente. Altrimenti, copia il -file d'esempio in `~/.config/sway/config`, generalmente è situato in -`/etc/sway/config`. Consulta `man 5 sway` per ulteriori informazioni sulla -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 -Lancia `sway` da un TTY o da un display manager. - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[canale IRC]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc +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 d6a8202e3..800daa62d 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1,18 +1,20 @@ # sway -Swayは[i3](https://i3wm.org/)互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。 +Swayはi3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。 [FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 -[IRC チャンネル](https://web.libera.chat/gamja/?channels=#sway) (irc.libera.chatの#sway)もあります。 +[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 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)から寄付ができます。誰でも報奨金を請求できますし、自分の欲しい機能に報奨金を懸ける事も出来ます。またSwayのメンテナンスを支援するには、Patreonがより有用です。 + ## 日本語サポート SirCmpwnは、日本語でのサポートをIRCとGitHubで行います。タイムゾーンはUTC-4です。 ## リリースの署名 -Swayのリリースは[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)で署名され、[GitHub](https://github.com/swaywm/sway/releases)で公開されています。 +Swayのリリースは[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)で署名され、[GitHub](https://github.com/swaywm/sway/releases)で公開されています。 ## インストール @@ -27,29 +29,38 @@ Swayは沢山のディストリビューションで提供されています。" 次の依存パッケージをインストールしてください: * meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) +* [wlroots](https://github.com/swaywm/wlroots) * wayland * wayland-protocols \* -* pcre2 +* pcre * json-c * pango * cairo -* gdk-pixbuf2 (任意: システムイコンで必要です) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (任意: manで必要です) \* -* git (任意: バージョン情報で必要です) \* +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) \* +* git \* -_\*コンパイル時の依存_ +_\*コンパイルの時_ + +_\*\*オプション: swaybgでのみ必要です_ 次のコマンドを実行してください: - meson setup build + meson build ninja -C build sudo ninja -C build install +logindを使用していないシステムでは、バイナリに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`を実行することで、設定に関する情報を見ることができます。 ## 実行 -`sway`をTTYまたはディスプレイマネージャから実行してください。 +`sway`をTTYから実行してください。いくつかのディスプレイマネージャは動くかもしれませんが、Swayからサポートされていません(gdmは非常に良く動作することが知られています)。 + diff --git a/README.ko.md b/README.ko.md deleted file mode 100644 index c9f31beff..000000000 --- a/README.ko.md +++ /dev/null @@ -1,55 +0,0 @@ -# sway - -sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) 컴포지터입니다. -[FAQ](https://github.com/swaywm/sway/wiki)를 읽어보세요. [IRC 채널](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)도 있습니다. - -## 릴리즈 서명 - -릴리즈는 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)에서 서명되고, -[GitHub에서](https://github.com/swaywm/sway/releases) 공개되고 있습니다. - -## 설치 - -### 패키지를 통한 설치 - -Sway는 많은 배포판에서 이용할 수 있습니다. "sway" 패키지를 설치해 보세요. -만약 없다면, [위키 페이지](https://github.com/swaywm/sway/wiki/Unsupported-packages)를 확인하세요. -해당 배포판에 대한 설치 정보를 확인할 수 있습니다. - -당신의 배포판에 sway 패키지를 제공하고 싶다면, -IRC 채널을 방문하거나 sir@cmpwn.com으로 이메일을 보내 상담 받으세요. - -### 소스를 통한 컴파일 - -다음 의존 패키지들을 설치해 주세요: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (선택: system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (선택: man pages) \* -* git \* - -_\*컴파일 떄 필요_ - -다음 명령을 실행하세요: - - meson setup build - ninja -C build - sudo ninja -C build install - -## 설정 - -i3를 이미 사용 중이라면, i3 config을 `~/.config/sway/config`로 복사하세요. -아니면, 샘플 구성 파일을 '~/.config/sway/config'에 복사할 수도 있습니다. -일반적으로 "/etc/sway/config"에 위치해 있습니다. -설정에 대한 정보를 보려면 "man 5 sway"를 실행하세요. - -## 실행 - -TTY나 display manager에서 `sway`를 실행하세요. diff --git a/README.md b/README.md index 882db57ee..239e7e3ef 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,63 @@ # sway -**[English][en]** - [عربي][ar] - [Azərbaycanca][az] - [Česky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Српски][sr] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] +[**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--) -sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the -[IRC channel] \(#sway on irc.libera.chat). +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). + +If you'd like to support sway development, please contribute to [SirCmpwn's +Patreon page](https://patreon.com/sircmpwn). ## Release Signatures -Releases are signed with [E88F5E48] and published [on GitHub][GitHub releases]. +Releases are signed with [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) +and published [on GitHub](https://github.com/swaywm/sway/releases). ## Installation ### From Packages Sway is available in many distributions. Try installing the "sway" package for -yours. +yours. If it's not available, check out [this wiki page](https://github.com/swaywm/sway/wiki/Unsupported-packages) +for information on installation for your distributions. + +If you're interested in packaging sway for your distribution, stop by the IRC +channel or shoot an email to sir@cmpwn.com for advice. ### Compiling from Source -Check out [this wiki page][Development setup] if you want to build the HEAD of -sway and wlroots for testing or development. - Install dependencies: * meson \* -* [wlroots] +* [wlroots](https://github.com/swaywm/wlroots) * wayland * wayland-protocols \* -* pcre2 +* pcre * json-c * pango * cairo -* gdk-pixbuf2 (optional: additional image formats for system tray) -* [swaybg] (optional: wallpaper) -* [scdoc] (optional: man pages) \* -* git (optional: version info) \* +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* git \* -_\* Compile-time dep_ +_\*Compile-time dep_ + +_\*\*optional: required for swaybg_ Run these commands: - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install + meson build + ninja -C build + sudo ninja -C build install + +On systems without logind, you need to suid the sway binary: + + sudo chmod a+s /usr/local/bin/sway + +Sway will drop root permissions shortly after startup. ## Configuration @@ -53,43 +68,5 @@ Run `man 5 sway` for information on the configuration. ## Running -Run `sway` from a TTY or from a display manager. - -[en]: https://github.com/swaywm/sway#readme -[ar]: README.ar.md -[az]: README.az.md -[cs]: README.cs.md -[de]: README.de.md -[dk]: README.dk.md -[es]: README.es.md -[fr]: README.fr.md -[ge]: README.ge.md -[gr]: README.gr.md -[hi]: README.hi.md -[hu]: README.hu.md -[ir]: README.ir.md -[it]: README.it.md -[ja]: README.ja.md -[ko]: README.ko.md -[nl]: README.nl.md -[no]: README.no.md -[pl]: README.pl.md -[pt]: README.pt.md -[ro]: README.ro.md -[ru]: README.ru.md -[sr]: README.sr.md -[sv]: README.sv.md -[tr]: README.tr.md -[uk]: README.uk.md -[zh-CN]: README.zh-CN.md -[zh-TW]: README.zh-TW.md -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[swaybg]: https://github.com/swaywm/swaybg/ -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc +Run `sway` from a TTY. Some display managers may work but are not supported by +sway (gdm is known to work fairly well). diff --git a/README.nl.md b/README.nl.md deleted file mode 100644 index f2f7bb497..000000000 --- a/README.nl.md +++ /dev/null @@ -1,57 +0,0 @@ -# sway - -Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor. -Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC -kanaal](https://web.libera.chat/gamja/?channels=#sway) (#sway op -irc.libera.chat). - -## Releasehandtekeningen - -Releases worden ondertekend met [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) -en gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases). - -## Installatie - -### Via een pakket - -Sway is beschikbaar in vele distributies. Probeer het "sway"-pakket te installeren met jouw pakketbeheerapplicatie. Als het niet beschikbaar is, bekijk dan [deze wikipagina](https://github.com/swaywm/sway/wiki/Unsupported-packages) -voor informatie over installatie in jouw distributie. - -Als je geïnteresseerd bent in het maken van pakketten voor je distributie, stuur een bericht in het IRC- -kanaal of stuur een e-mail naar sir@cmpwn.com voor advies. - -### Compilatie vanuit broncode - -Afhankelijkheden installeren: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (optioneel: systeemtray) -* [scdoc](https://git.sr.ht/~ircmpwn/scdoc) (optioneel: manpagina's) \* -* git \* - -_\* Compileerafhankelijkheden_ - -Voer deze opdrachten uit: - - meson setup build - ninja -C build - sudo ninja -C build install - -## Configuratie - -Als je al i3 gebruikt, kopieer dan je i3-configuratie naar `~/.config/sway/config` en -het zal zonder verdere configuratie werken. Kopieer anders het voorbeeldconfiguratiebestand naar -`~/.config/sway/config`. Dit is meestal het bestand: `/etc/sway/config`. -Voer `man 5 sway` uit voor informatie over het configureren van sway. - -## Uitvoeren - -Voer `sway` vanaf een TTY uit. Sommige display-managers kunnen werken, maar worden niet ondersteund door -sway (van gdm is bekend dat het redelijk goed werkt). diff --git a/README.no.md b/README.no.md deleted file mode 100644 index 77676062d..000000000 --- a/README.no.md +++ /dev/null @@ -1,65 +0,0 @@ -# Sway - -Sway er en [i3]-kompatibel [Wayland]-compositor. Les [Ofte stilte spørsmål]. -Delta på [IRC-kanalen][IRC-kanal] \(#sway på irc.libera.chat). - -## Signaturer - -Utgivelser er signert med [E88F5E48] og publisert [på GitHub][GitHub releases]. - -## Installasjon - -### Fra systempakker - -Sway er tilgjengelig i mange distribusjoner. Prøv å installere pakken "sway" -fra din distro sine repoer. - -### Kompilering fra kildekode - -Se [denne wiki-siden][Oppsetting for utvikling] hvis du vil bygge fra HEAD-grenen av -sway og wlroots for testing eller utvikling. - -Installer avhengigheter: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (valgfritt: støtte for ekstra bildeformater i system tray) -* [swaybg] (valgfritt: bakgrunnsbilde) -* [scdoc] (valgfritt: man pages) \* -* git (valgfritt: versjonsinformasjon) \* - -_\* Kompileringsavhengigheter_ - -Kjør følgende kommandoer: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## Konfigurasjon - -Hvis du allerede bruker i3 kan du bare kopiere din i3-konfigurasjon til -`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurasjonsfilen til -`~/.config/sway/config`. Eksempelfilen er normalt plasert i `/etc/sway/config`. -Kjør `man 5 sway` for å få opplysninger om konfigurasjonen. - -## Kjøring - -Kjør `sway` fra en TTY eller fra en display manager. - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[Ofte stilte spørsmål]: https://github.com/swaywm/sway/wiki -[IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Oppsetting for utvikling]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[swaybg]: https://github.com/swaywm/swaybg/ -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.pl.md b/README.pl.md deleted file mode 100644 index 0c12a2b86..000000000 --- a/README.pl.md +++ /dev/null @@ -1,56 +0,0 @@ -# sway - -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](https://web.libera.chat/gamja/?channels=#sway) -(#sway na irc.libera.chat). - -## Podpisy cyfrowe wydań - -Wydania są podpisywane przy pomocy klucza [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) -i publikowane [na GitHubie](https://github.com/swaywm/sway/releases). - -## Instalacja - -### Z pakietów - -Sway jest dostępny w wielu dystybucjach. Spróbuj zainstalować pakiet "sway" w swoim -menedżerze pakietów. Jeśli nie jest dostępny, sprawdź [tę stronę wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) -aby uzyskać informacje dotyczące instalacji w swojej dystrybucji. - -Jeśli chciałbyś stworzyć pakiet sway dla swojej dystrybucji, odwiedź kanał IRC lub wyślij email na -adres sir@cmpwn.com w celu uzyskania wskazówek. - -### Kompilacja ze Źródła - -Zainstaluj zależności: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (opcjonalnie: system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opcjonalnie: strony pomocy man) \* -* git \* - -_\*zależności kompilacji_ - -Wykonaj następujące polecenia: - - meson setup build - ninja -C build - sudo ninja -C build install - -## Konfiguracja - -Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i -zadziała od ręki. W przeciwnym razie skopiuj przykładowy plik konfiguracyjny do folderu -`~/.config/sway/config`; zazwyczaj znajduje się w `/etc/sway/config`. -Wykonaj polecenie `man 5 sway` aby uzyskać informacje dotyczące konfiguracji. - -## Uruchamianie - -Wykonaj polecenie `sway` z poziomu TTY lub menedżera wyświetlania. diff --git a/README.pt.md b/README.pt.md index 4660e0bed..0f2a3cd49 100644 --- a/README.pt.md +++ b/README.pt.md @@ -1,59 +1,91 @@ # sway -O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatível com o [i3](https://i3wm.org/). -Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do -IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway em -irc.libera.chat). +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). -## Assinatura das versões +[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) -As versões são assinadas com [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) +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 -O Sway está disponível em várias distribuições. Tente instalar o pacote "sway" -na sua. Caso não esteja disponível, verifique [esta wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) -para se informar a sobre a instalação para sua distribuição. -Se você está interessado em criar um pacote do sway para a sua distribuição, verifique canal do IRC -ou mande um email para sir@cmpwn.com para obter informações. +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). -### Compilando a partir do código-fonte +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*). -Verifique [essa página da wiki](https://github.com/swaywm/sway/wiki/Development-Setup) se você quer compilar o HEAD do sway e o wlroots para testes ou desenvolvimento. +### A partir do código-fonte -Instale as dependências: +Antes de iniciar a compilação, instale as dependências: -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) +* meson +* [wlc](https://github.com/Cloudef/wlc) * wayland -* wayland-protocols \* -* pcre2 -* json-c +* xwayland +* libinput >= 1.6.0 +* libcap +* pcre +* json-c >= 0.13 * pango * cairo -* gdk-pixbuf2 (opcional: system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opcional: man pages) \* -* git (opcional: informações de versão) \* +* gdk-pixbuf2 * +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (man pages) +* git -_\*Dependência de tempo de compilação_ +_\*Dependência apenas de swaybar, swaybg_ -Execute esses comandos: +Para compilar, execute estes comandos: - meson setup build + 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á utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e -tudo funcionará normalmente. Caso contrário, copie o arquivo de configuração de exemplo para -`~/.config/sway/config`. Normalmente, este arquivo está localizado em `/etc/sway/config`. -Execute `man 5 sway` para se informar sobre a 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. -## Execuçã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). -Execute o comando `sway` de um TTY. Alguns gerenciadores de display (ou gerenciadores de login) podem funcionar mas alguns não são suportados -pelo sway (o gdm é conhecido por funcionar bem). diff --git a/README.ro.md b/README.ro.md deleted file mode 100644 index 1f63b5955..000000000 --- a/README.ro.md +++ /dev/null @@ -1,53 +0,0 @@ -# sway - -sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/). -Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway pe irc.libera.chat). - -## Semnarea digitală - -Noile versiuni sunt semnate cu [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) -și postate [pe GitHub](https://github.com/swaywm/sway/releases). - -## Instalare - -### Din pachete (packages) - -sway este disponibil în multe distribuții. Încercați să instalați pachetul "sway" pe distribuția voastră. Dacă nu este disponibil, uitați-vă în [această pagină wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) -pentru informații a cum puteți să instalați pentru distribuția voastră. - -Dacă sunteți interesați in a crea pachete pentru distribuția voastră, informați-ne prin IRC sau contactați prin email pe sir@cmpwn.com pentru ajutor. - -### Compilare din sursă - -Dependențe pentru instalare: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (opțional, dacă doriți să aveți system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opțional, pentru paginile man) \* -* git (opțional, pentru informații de versiune) \* - -*Dependențe doar pentru compilare* - -Rulați aceste comenzi: - -``` - meson setup build - ninja -C build - sudo ninja -C build install -``` - -## Configurare - -Dacă folosiți deja i3, copiați fișierul de configurare din i3 în `~/.config/sway/config`, și va funcționa fără a necesita nici o modificare. In caz contrar, copiați exemplul de configurare (disponibil de obicei în `/etc/sway/config`) în `~/.config/sway/config`. -Folosiți comanda `man 5 sway` pentru informații despre configurare. - -## Lansare - -Folosiți comanda `sway` într-un TTY. Managerii de display nu sunt suportați de către Sway, dar unii pot functiona (se știe că gdm functioneazâ destul de bine). diff --git a/README.ru.md b/README.ru.md index 5eee59efa..c66976e58 100644 --- a/README.ru.md +++ b/README.ru.md @@ -1,67 +1,83 @@ # sway -sway - это [i3]-совместимый композитор [Wayland]. -Больше информации в [FAQ]. Присоединяйтесь к -[IRC-каналу][IRC channel] (#sway на -irc.libera.chat). +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) -Релизы подписываются ключом [E88F5E48] и публикуются [на GitHub][GitHub releases]. +При желании поддержать разработку 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". +Sway доступен во многих дистрибутивах и находится в официальных репозиториях. Попробуйте установить "sway" через ваш пакетный менеджер. +В случае, если это не представляется возможным, то обратитесь к [этой странице](https://github.com/swaywm/sway/wiki/Unsupported-packages) +для получения инструкций по установке для вашего дистрибутива. -Если вас интересует создание пакета sway для вашего дистрибутива, зайдите на [IRC-канал][IRC channel] -или отправьте письмо на sir@cmpwn.com за советом. +Если вы заинтересованы в создании пакета "sway" в вашем дистрибутиве, то сообщите об этом в IRC +канале или отправьте письмо [sir@cmpwn.com](mailto:sir@cmpwn.com). ### Сборка из исходников -Посетите [эту страницу на вики][Development setup], если вы хотите построить последнюю версию -sway и wlroots для тестирования или разработки. +Установите следующие пакеты: -Установите зависимости: - -* meson \* -* [wlroots] +* meson +* [wlc](https://github.com/Cloudef/wlc) * wayland -* wayland-protocols \* -* pcre2 -* json-c +* xwayland +* libinput >= 1.6.0 +* libcap +* pcre +* json-c >= 0.13 * pango * cairo -* gdk-pixbuf2 (опционально: для работы трея) -* [scdoc] (опционально: для man-страниц) \* -* git (опционально: для информации о версии) \* +* gdk-pixbuf2 * +* dbus >= 1.10 ** +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) +* git -_\*Зависимости для сборки_ +_\*Требуется только для swaybar, swaybg_ -Выполните эти команды: +_\*\*Требуется только для tray_ - meson setup build +Выполните следующие команды: + + 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, скопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и -он сразу же заработает. В противном случае, скопируйте образец конфигурационного файла в -`~/.config/sway/config`. Обычно он располагается в `/etc/sway/config`. -Запустите `man 5 sway` для изучения информации о настройке. +Если вы уже используете i3, тогда просто скопируйте ваш конфиг в `~/.config/sway/config`. +В любом другом случае, скопируйте `/etc/sway/config` в `~/.config/sway/config`. +Для более детальной информации о настройке: `man 5 sway`. ## Запуск -Выполните команду `sway` прямо из TTY или дисплейного менеджера. - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc +Выполните 'sway' в терминале. **Некоторые** менеджеры сессий могут работать, но не поддерживаются sway (к примеру, gdm работает со sway без проблем). diff --git a/README.sr.md b/README.sr.md deleted file mode 100644 index bd573566a..000000000 --- a/README.sr.md +++ /dev/null @@ -1,65 +0,0 @@ -# sway - -sway је [i3]-компатибилан [Wayland] композитор. Прочитајте [FAQ]. Придружите се -[IRC каналу] \(#sway на irc.libera.chat). - -## Потписи Издања - -Издања су потписана са [E88F5E48] и објављена [на GitHub-у][GitHub releases]. - -## Инсталација - -### Из пакета - -Sway је доступан у многим дистрибуцијама. Покушајте да инсталирате "sway" пакет за -вашу. - -### Компајлирање из Извора - -Погледајте [ову вики страницу][Development setup], ако желите да компајлирате HEAD верзију -sway-а и wlroots-а за тестирање или развој. - -Инсталирајте зависности: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (опционо: додатни формати слика за системску траку) -* [swaybg] (опционо: позадина) -* [scdoc] (опционо: man странице) \* -* git (опционо: информације о верзији) \* - -_\* Потребно само за компајлирање_ - -Покрените следеће команде: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## Конфигурација - -Ако већ користите i3, копирајте вашу i3 конфигурацију у `~/.config/sway/config` и -радиће одмах. У супротном, копирајте пример конфигурационе датотеке у -`~/.config/sway/config`. Обично се налази у `/etc/sway/config`. -Покрените `man 5 sway` за информације о конфигурацији. - -## Покретање - -Покрените `sway` из TTY-a или из менаџера приказа. - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC каналу]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[swaybg]: https://github.com/swaywm/swaybg/ -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.sv.md b/README.sv.md deleted file mode 100644 index bff9d4a85..000000000 --- a/README.sv.md +++ /dev/null @@ -1,81 +0,0 @@ -# sway - -sway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår -[IRC-kanal] \(#sway på irc.libera.chat). - -## Utgåvosignaturer - -Utgåvor är signerade med [E88F5E48] och publicerade på [GitHub][GitHub releases]. - -## Installering - -### Med pakethanterare - -Sway är tillgänglig i många distributioner. Prova att installera "sway" med din distributions pakethanterare. - -### Genom att kompilera från källkod - -Kolla in [denna wiki-sida][Development setup] om du vill bygga sway och wlroots HEAD för testning eller utveckling. - -Installera paket som sway behöver: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (valbar: systembricka) -* [scdoc] (valbar: manualer) \* -* git (valbar: versioninfo) \* - -_\* Krav för kompilering_ - -Kör dessa kommandon: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## Konfiguration - -Ifall du redan använder i3 så kan du kopiera din konfigurationsfil till `~/.config/sway/config` och det kommer då att fungera som det ska. -Kopiera annars exemplarkonfigurationsfilen till `~/.config/sway/config`. Den ligger oftast i `/etc/sway/config`. -Kör `man 5 sway` för mer information kring konfigurationen. - -## Att köra sway - -Kör `sway` från en TTY. Vissa inloggningahanterare kan fungera men inte vara stödda av sway (gdm ska fungera hyfsat bra). - -[en]: https://github.com/swaywm/sway#readme -[de]: README.de.md -[dk]: README.dk.md -[es]: README.es.md -[fr]: README.fr.md -[sv]: README.sv.md -[gr]: README.gr.md -[hu]: README.hu.md -[ir]: README.ir.md -[it]: README.it.md -[ja]: README.ja.md -[ko]: README.ko.md -[nl]: README.nl.md -[pl]: README.pl.md -[pt]: README.pt.md -[ro]: README.ro.md -[ru]: README.ru.md -[tr]: README.tr.md -[uk]: README.uk.md -[zh-CN]: README.zh-CN.md -[zh-TW]: README.zh-TW.md -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.tr.md b/README.tr.md deleted file mode 100644 index 3769ee755..000000000 --- a/README.tr.md +++ /dev/null @@ -1,62 +0,0 @@ -# sway - - -Sway, [i3]-uyumlu bir [Wayland] dizgicisidir. [SSS][FAQ]'yi okuyun. -[IRC kanalı][IRC channel]na katılın \(irc.libera.chat'te #sway (İngilizce)). - -## Sürüm imzaları - -Sürümler [E88F5E48] ile imzalandı ve [GitHub][GitHub releases]'da yayınlandı. - -## Kurulum - -### Paketler ile - -Sway birçok dağıtımda mevcuttur. Sizinki için "sway" paketini yüklemeyi deneyin. - -Dağıtımınız için sway'i paketlemekle ilgileniyorsanız, IRC kanalına uğrayın veya tavsiye için sir@cmpwn.com adresine bir e-posta gönderin. - -### Kaynak koddan derleme - -Test veya geliştirme için sway ve wlroots'un HEAD'ini oluşturmak istiyorsanız [bu wiki sayfası][Development setup]na göz atın. - -Aşağıdaki bağımlılıkları yükleyin: - -* meson \* -* [wlroots] -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (isteğe bağlı: system tray) -* [scdoc] (isteğe bağlı: man pages) \* -* git (isteğe bağlı: version info) \* - -_\*Derleme-anı bağımlılıkları_ - -Şu komutları çalıştırın: - - meson setup build - ninja -C build - sudo ninja -C build install - -## Yapılandırma - -Zaten i3 kullanıyorsanız, i3 yapılandırmanızı `~/.config/sway/config` konumuna kopyalayın ve kutudan çıktığı gibi çalışacaktır. Aksi takdirde, örnek yapılandırma dosyasını `~/.config/sway/config` konumuna kopyalayın. Genellikle `/etc/sway/config` konumunda bulunur. -Yapılandırma hakkında bilgi almak için `man 5 sway` komutunu çalıştırın. - -## Çalıştırma - -TTY'den `sway` çalıştırın. Bazı görüntü yöneticileriyle(display manager) çalışabilir ama Sway tarafından desteklenmez. (gdm'nin oldukça iyi çalıştığı bilinmektedir.) - -[i3]: https://i3wm.org/ -[Wayland]: http://wayland.freedesktop.org/ -[FAQ]: https://github.com/swaywm/sway/wiki -[IRC channel]: https://web.libera.chat/gamja/?channels=#sway -[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 -[GitHub releases]: https://github.com/swaywm/sway/releases -[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots -[scdoc]: https://git.sr.ht/~sircmpwn/scdoc diff --git a/README.uk.md b/README.uk.md index 01c43afee..4c378a89f 100644 --- a/README.uk.md +++ b/README.uk.md @@ -1,9 +1,20 @@ # sway -Sway це сумісний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). -Ознайомтесь з [ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в -IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway на -irc.libera.chat). +**Sway** це сумісний з i3 композитор [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. ## Підтримка українською мовою @@ -15,7 +26,7 @@ Hummer12007 у IRC-спільноті. Будьте терплячі, вам о ## Підписи випусків -Випуски підписані ключем [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) +Випуски підписані ключем [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) та публікуються на сторінці [GitHub](https://github.com/swaywm/sway/releases). ## Встановлення @@ -28,33 +39,44 @@ Sway доступний у багатьох дистрибутивах Linux (а для інформації щодо встановлення на вашому дистрибутиві. Якщо ви готові та зацікавлені запакувати і підтримувати Sway у вашому -дистрибутиві, звертайтесь за порадами до нашого каналу в IRC або -пишіть на електронну пошту [sir@cmpwn.com](mailto:sir@cmpwn.com). +дистрибутиві, будемо раді вас бачити у нашому каналі IRC. Ви також можете +спитати порад за адресою sir@cmpwn.com. ### З вихідного коду Встановіть залежності: -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) +* meson +* [wlc](https://github.com/Cloudef/wlc) * wayland -* wayland-protocols \* -* pcre2 -* json-c +* xwayland +* libinput >= 1.6.0 +* libcap +* pcre +* json-c >= 0.13 * pango * cairo -* gdk-pixbuf2 (optional: system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (необов'язково, необхідно для сторінок man) \* -* git \* +* gdk-pixbuf2 * +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) +* git -_\*Лише для компіляції_ +_\*Лише для swaybar, swaybg_ Виконайте ці команди: - meson setup build + meson build 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: + + sudo chmod a+s /usr/local/bin/sway + ## Налаштування Якщо ви вже використовуєте i3, скопіюйте свій файл налаштувань diff --git a/README.zh-CN.md b/README.zh-CN.md deleted file mode 100644 index b733c71ab..000000000 --- a/README.zh-CN.md +++ /dev/null @@ -1,50 +0,0 @@ -# sway - -sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor。 - [查看FAQ](https://github.com/swaywm/sway/wiki)/ [加入IRC频道](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat) - -## 发行签名 - -每个发行版都以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 的密钥签名并发布在 [GitHub](https://github.com/swaywm/sway/releases)上。 - -## 安装 - -### 从包管理器安装 - -Sway 在很多发行版中可用。请尝试在你的发行版中安装 `sway` 。 - -### 从源码编译 - -如果想要构建最新版sway和wlroots用以测试和开发,请查看 [此wiki页面](https://github.com/swaywm/sway/wiki/Development-Setup) - -安装如下依赖: - -* meson \* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (可选的: system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (可选: man pages) \* -* git \* - -_\*编译时依赖_ - -运行如下命令: - - meson setup build/ - ninja -C build/ - sudo ninja -C build/ install - -## 配置 - -如果你已经在使用i3,直接复制i3配置文件到 `~/.config/sway/config`,这是开箱即用的。或者,你可以复制配置样例到`~/.config/sway/config`。它通常位于 `/etc/sway/config`。 -运行 `man 5 sway` 获取关于配置的更多信息。 - -## 运行 - -从 TTY 中运行 `sway`。 某些显示管理器(Display Manager)也许可以工作但不被 sway 支持。 -(已知 gdm 工作得非常好)。 diff --git a/README.zh-TW.md b/README.zh-TW.md deleted file mode 100644 index 7cf848ea3..000000000 --- a/README.zh-TW.md +++ /dev/null @@ -1,55 +0,0 @@ -# sway - -sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。 -閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 加入 [IRC -頻道](https://web.libera.chat/gamja/?channels=#sway) (#sway on -irc.libera.chat) - -## 發行簽章 - -所有發行的版本都會以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 簽署 -並發佈於 [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://gitlab.freedesktop.org/wlroots/wlroots) -* wayland -* wayland-protocols \* -* pcre2 -* json-c -* pango -* cairo -* gdk-pixbuf2 (選擇性: system tray) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (選擇性: man pages) \* -* git \* - -_\*編譯時相依_ - -執行這些指令: - - meson setup build - ninja -C build - sudo ninja -C build install - -## 設定檔 - -如果你已經在使用 i3,你可以直接將你的 i3 設定檔複製到 `~/.config/sway/config` 然後就能直接使用。 -或者你也可以把範例設定檔複製到 `~/.config/sway/config`。 它通常會在 `/etc/sway/config`。 -執行 `man 5 sway` 來取得更多關於設定檔的資訊。 - -## 執行 - -在 TTY 執行 `sway`。有些 display manager 可能可以運作但 sway 不提供支援 (已知 gdm 運作的很好) diff --git a/assets/LICENSE b/assets/LICENSE deleted file mode 100644 index 0e259d42c..000000000 --- a/assets/LICENSE +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/client/meson.build b/client/meson.build index 4484a38c7..abe6f1eba 100644 --- a/client/meson.build +++ b/client/meson.build @@ -5,6 +5,7 @@ lib_sway_client = static_library( ), dependencies: [ cairo, + gdk_pixbuf, pango, pangocairo, wayland_client diff --git a/client/pool-buffer.c b/client/pool-buffer.c index c47c40ebc..836c6b136 100644 --- a/client/pool-buffer.c +++ b/client/pool-buffer.c @@ -1,42 +1,62 @@ +#define _POSIX_C_SOURCE 200809 #include -#include -#include +#include #include #include #include #include #include #include -#include #include #include #include "config.h" #include "pool-buffer.h" -#include "util.h" -static int anonymous_shm_open(void) { - int retries = 100; +static bool set_cloexec(int fd) { + long flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return false; + } - do { - // try a probably-unique name - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - pid_t pid = getpid(); - char name[50]; - snprintf(name, sizeof(name), "/sway-%x-%x", - (unsigned int)pid, (unsigned int)ts.tv_nsec); + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + return false; + } - // shm_open guarantees that O_CLOEXEC is set - int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd >= 0) { - shm_unlink(name); - return fd; - } + return true; +} - --retries; - } while (retries > 0 && errno == EEXIST); +static int create_pool_file(size_t size, char **name) { + static const char template[] = "sway-client-XXXXXX"; + const char *path = getenv("XDG_RUNTIME_DIR"); + if (path == NULL) { + fprintf(stderr, "XDG_RUNTIME_DIR is not set\n"); + return -1; + } - return -1; + size_t name_size = strlen(template) + 1 + strlen(path) + 1; + *name = malloc(name_size); + if (*name == NULL) { + fprintf(stderr, "allocation failed\n"); + return -1; + } + snprintf(*name, name_size, "%s/%s", path, template); + + int fd = mkstemp(*name); + if (fd < 0) { + return -1; + } + + if (!set_cloexec(fd)) { + close(fd); + return -1; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; } static void buffer_release(void *data, struct wl_buffer *wl_buffer) { @@ -54,20 +74,17 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm, uint32_t stride = width * 4; size_t size = stride * height; - int fd = anonymous_shm_open(); - if (fd == -1) { - return NULL; - } - if (ftruncate(fd, size) < 0) { - close(fd); - return NULL; - } + char *name; + int fd = create_pool_file(size, &name); + assert(fd != -1); void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); buf->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format); wl_shm_pool_destroy(pool); close(fd); + unlink(name); + free(name); buf->size = size; buf->width = width; diff --git a/common/background-image.c b/common/background-image.c new file mode 100644 index 000000000..4431b725e --- /dev/null +++ b/common/background-image.c @@ -0,0 +1,120 @@ +#include +#include "background-image.h" +#include "cairo.h" +#include "log.h" + +enum background_mode parse_background_mode(const char *mode) { + if (strcmp(mode, "stretch") == 0) { + return BACKGROUND_MODE_STRETCH; + } else if (strcmp(mode, "fill") == 0) { + return BACKGROUND_MODE_FILL; + } else if (strcmp(mode, "fit") == 0) { + return BACKGROUND_MODE_FIT; + } else if (strcmp(mode, "center") == 0) { + return BACKGROUND_MODE_CENTER; + } else if (strcmp(mode, "tile") == 0) { + return BACKGROUND_MODE_TILE; + } else if (strcmp(mode, "solid_color") == 0) { + return BACKGROUND_MODE_SOLID_COLOR; + } + sway_log(SWAY_ERROR, "Unsupported background mode: %s", mode); + return BACKGROUND_MODE_INVALID; +} + +cairo_surface_t *load_background_image(const char *path) { + cairo_surface_t *image; +#if HAVE_GDK_PIXBUF + GError *err = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); + if (!pixbuf) { + sway_log(SWAY_ERROR, "Failed to load background image (%s).", + err->message); + return NULL; + } + image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); + g_object_unref(pixbuf); +#else + image = cairo_image_surface_create_from_png(path); +#endif // HAVE_GDK_PIXBUF + if (!image) { + sway_log(SWAY_ERROR, "Failed to read background image."); + return NULL; + } + if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "Failed to read background image: %s." +#if !HAVE_GDK_PIXBUF + "\nSway was compiled without gdk_pixbuf support, so only" + "\nPNG images can be loaded. This is the likely cause." +#endif // !HAVE_GDK_PIXBUF + , cairo_status_to_string(cairo_surface_status(image))); + return NULL; + } + return image; +} + +void render_background_image(cairo_t *cairo, cairo_surface_t *image, + enum background_mode mode, int buffer_width, int buffer_height) { + double width = cairo_image_surface_get_width(image); + double height = cairo_image_surface_get_height(image); + + cairo_save(cairo); + switch (mode) { + case BACKGROUND_MODE_STRETCH: + cairo_scale(cairo, + (double)buffer_width / width, + (double)buffer_height / height); + cairo_set_source_surface(cairo, image, 0, 0); + break; + case BACKGROUND_MODE_FILL: { + double window_ratio = (double)buffer_width / buffer_height; + double bg_ratio = width / height; + + if (window_ratio > bg_ratio) { + double scale = (double)buffer_width / width; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + 0, (double)buffer_height / 2 / scale - height / 2); + } else { + double scale = (double)buffer_height / height; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + (double)buffer_width / 2 / scale - width / 2, 0); + } + break; + } + case BACKGROUND_MODE_FIT: { + double window_ratio = (double)buffer_width / buffer_height; + double bg_ratio = width / height; + + if (window_ratio > bg_ratio) { + double scale = (double)buffer_height / height; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + (double)buffer_width / 2 / scale - width / 2, 0); + } else { + double scale = (double)buffer_width / width; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + 0, (double)buffer_height / 2 / scale - height / 2); + } + break; + } + case BACKGROUND_MODE_CENTER: + cairo_set_source_surface(cairo, image, + (double)buffer_width / 2 - width / 2, + (double)buffer_height / 2 - height / 2); + break; + case BACKGROUND_MODE_TILE: { + cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + cairo_set_source(cairo, pattern); + break; + } + case BACKGROUND_MODE_SOLID_COLOR: + case BACKGROUND_MODE_INVALID: + assert(0); + break; + } + cairo_paint(cairo); + cairo_restore(cairo); +} diff --git a/common/cairo.c b/common/cairo.c index 7c59d48c4..f2ad54c18 100644 --- a/common/cairo.c +++ b/common/cairo.c @@ -1,6 +1,9 @@ #include -#include -#include "cairo_util.h" +#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, @@ -42,3 +45,99 @@ 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/gesture.c b/common/gesture.c deleted file mode 100644 index 272aa8374..000000000 --- a/common/gesture.c +++ /dev/null @@ -1,332 +0,0 @@ -#include "gesture.h" - -#include -#include -#include -#include -#include -#include "list.h" -#include "log.h" -#include "stringop.h" - -const uint8_t GESTURE_FINGERS_ANY = 0; - -char *gesture_parse(const char *input, struct gesture *output) { - // Clear output in case of failure - output->type = GESTURE_TYPE_NONE; - output->fingers = GESTURE_FINGERS_ANY; - output->directions = GESTURE_DIRECTION_NONE; - - // Split input type, fingers and directions - list_t *split = split_string(input, ":"); - if (split->length < 1 || split->length > 3) { - return format_str( - "expected [:][:direction], got %s", - input); - } - - // Parse gesture type - if (strcmp(split->items[0], "hold") == 0) { - output->type = GESTURE_TYPE_HOLD; - } else if (strcmp(split->items[0], "pinch") == 0) { - output->type = GESTURE_TYPE_PINCH; - } else if (strcmp(split->items[0], "swipe") == 0) { - output->type = GESTURE_TYPE_SWIPE; - } else { - return format_str("expected hold|pinch|swipe, got %s", - (const char *)split->items[0]); - } - - // Parse optional arguments - if (split->length > 1) { - char *next = split->items[1]; - - // Try to parse as finger count (1-9) - if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') { - output->fingers = atoi(next); - - // Move to next if available - next = split->length == 3 ? split->items[2] : NULL; - } else if (split->length == 3) { - // Fail here if argument can only be finger count - return format_str("expected 1-9, got %s", next); - } - - // If there is an argument left, try to parse as direction - if (next) { - list_t *directions = split_string(next, "+"); - - for (int i = 0; i < directions->length; ++i) { - const char *item = directions->items[i]; - if (strcmp(item, "any") == 0) { - continue; - } else if (strcmp(item, "up") == 0) { - output->directions |= GESTURE_DIRECTION_UP; - } else if (strcmp(item, "down") == 0) { - output->directions |= GESTURE_DIRECTION_DOWN; - } else if (strcmp(item, "left") == 0) { - output->directions |= GESTURE_DIRECTION_LEFT; - } else if (strcmp(item, "right") == 0) { - output->directions |= GESTURE_DIRECTION_RIGHT; - } else if (strcmp(item, "inward") == 0) { - output->directions |= GESTURE_DIRECTION_INWARD; - } else if (strcmp(item, "outward") == 0) { - output->directions |= GESTURE_DIRECTION_OUTWARD; - } else if (strcmp(item, "clockwise") == 0) { - output->directions |= GESTURE_DIRECTION_CLOCKWISE; - } else if (strcmp(item, "counterclockwise") == 0) { - output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; - } else { - return format_str("expected direction, got %s", item); - } - } - list_free_items_and_destroy(directions); - } - } // if optional args - - list_free_items_and_destroy(split); - - return NULL; -} - -const char *gesture_type_string(enum gesture_type type) { - switch (type) { - case GESTURE_TYPE_NONE: - return "none"; - case GESTURE_TYPE_HOLD: - return "hold"; - case GESTURE_TYPE_PINCH: - return "pinch"; - case GESTURE_TYPE_SWIPE: - return "swipe"; - } - - return NULL; -} - -const char *gesture_direction_string(enum gesture_direction direction) { - switch (direction) { - case GESTURE_DIRECTION_NONE: - return "none"; - case GESTURE_DIRECTION_UP: - return "up"; - case GESTURE_DIRECTION_DOWN: - return "down"; - case GESTURE_DIRECTION_LEFT: - return "left"; - case GESTURE_DIRECTION_RIGHT: - return "right"; - case GESTURE_DIRECTION_INWARD: - return "inward"; - case GESTURE_DIRECTION_OUTWARD: - return "outward"; - case GESTURE_DIRECTION_CLOCKWISE: - return "clockwise"; - case GESTURE_DIRECTION_COUNTERCLOCKWISE: - return "counterclockwise"; - } - - return NULL; -} - -// Helper to turn combination of directions flags into string representation. -static char *gesture_directions_to_string(uint32_t directions) { - char *result = NULL; - - for (uint8_t bit = 0; bit < 32; bit++) { - uint32_t masked = directions & (1 << bit); - if (masked) { - const char *name = gesture_direction_string(masked); - - if (!name) { - name = "unknown"; - } - - if (!result) { - result = strdup(name); - } else { - char *new = format_str("%s+%s", result, name); - free(result); - result = new; - } - } - } - - if(!result) { - return strdup("any"); - } - - return result; -} - -char *gesture_to_string(struct gesture *gesture) { - char *directions = gesture_directions_to_string(gesture->directions); - char *result = format_str("%s:%u:%s", - gesture_type_string(gesture->type), - gesture->fingers, directions); - free(directions); - return result; -} - -bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) { - // Check that gesture type matches - if (target->type != type) { - return false; - } - - // Check that finger count matches - if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) { - return false; - } - - return true; -} - -bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) { - // Check type and fingers - if (!gesture_check(target, to_match->type, to_match->fingers)) { - return false; - } - - // Enforce exact matches ... - if (exact && target->directions != to_match->directions) { - return false; - } - - // ... or ensure all target directions are matched - return (target->directions & to_match->directions) == target->directions; -} - -bool gesture_equal(struct gesture *a, struct gesture *b) { - return a->type == b->type - && a->fingers == b->fingers - && a->directions == b->directions; -} - -// Return count of set bits in directions bit field. -static uint8_t gesture_directions_count(uint32_t directions) { - uint8_t count = 0; - for (; directions; directions >>= 1) { - count += directions & 1; - } - return count; -} - -// Compare direction bit count of two direction. -static int8_t gesture_directions_compare(uint32_t a, uint32_t b) { - return gesture_directions_count(a) - gesture_directions_count(b); -} - -// Compare two direction based on their direction bit count -int8_t gesture_compare(struct gesture *a, struct gesture *b) { - return gesture_directions_compare(a->directions, b->directions); -} - -void gesture_tracker_begin(struct gesture_tracker *tracker, - enum gesture_type type, uint8_t fingers) { - tracker->type = type; - tracker->fingers = fingers; - - tracker->dx = 0.0; - tracker->dy = 0.0; - tracker->scale = 1.0; - tracker->rotation = 0.0; - - sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture", - gesture_type_string(type), fingers); -} - -bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) { - return tracker->type == type; -} - -void gesture_tracker_update(struct gesture_tracker *tracker, - double dx, double dy, double scale, double rotation) { - if (tracker->type == GESTURE_TYPE_HOLD) { - sway_assert(false, "hold does not update."); - return; - } - - tracker->dx += dx; - tracker->dy += dy; - - if (tracker->type == GESTURE_TYPE_PINCH) { - tracker->scale = scale; - tracker->rotation += rotation; - } - - sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f", - gesture_type_string(tracker->type), - tracker->fingers, - tracker->dx, tracker->dy, - tracker->scale, tracker->rotation); -} - -void gesture_tracker_cancel(struct gesture_tracker *tracker) { - sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture", - gesture_type_string(tracker->type), tracker->fingers); - - tracker->type = GESTURE_TYPE_NONE; -} - -struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) { - struct gesture *result = calloc(1, sizeof(struct gesture)); - - // Ignore gesture under some threshold - // TODO: Make configurable - const double min_rotation = 5; - const double min_scale_delta = 0.1; - - // Determine direction - switch(tracker->type) { - // Gestures with scale and rotation - case GESTURE_TYPE_PINCH: - if (tracker->rotation > min_rotation) { - result->directions |= GESTURE_DIRECTION_CLOCKWISE; - } - if (tracker->rotation < -min_rotation) { - result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; - } - - if (tracker->scale > (1.0 + min_scale_delta)) { - result->directions |= GESTURE_DIRECTION_OUTWARD; - } - if (tracker->scale < (1.0 - min_scale_delta)) { - result->directions |= GESTURE_DIRECTION_INWARD; - } - __attribute__ ((fallthrough)); - // Gestures with dx and dy - case GESTURE_TYPE_SWIPE: - if (fabs(tracker->dx) > fabs(tracker->dy)) { - if (tracker->dx > 0) { - result->directions |= GESTURE_DIRECTION_RIGHT; - } else { - result->directions |= GESTURE_DIRECTION_LEFT; - } - } else { - if (tracker->dy > 0) { - result->directions |= GESTURE_DIRECTION_DOWN; - } else { - result->directions |= GESTURE_DIRECTION_UP; - } - } - // Gesture without any direction - case GESTURE_TYPE_HOLD: - break; - // Not tracking any gesture - case GESTURE_TYPE_NONE: - sway_assert(false, "Not tracking any gesture."); - return result; - } - - result->type = tracker->type; - result->fingers = tracker->fingers; - - char *description = gesture_to_string(result); - sway_log(SWAY_DEBUG, "end tracking gesture: %s", description); - free(description); - - tracker->type = GESTURE_TYPE_NONE; - - return result; -} diff --git a/common/ipc-client.c b/common/ipc-client.c index a0be2b2d9..13e2dfa32 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -68,16 +69,9 @@ 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)); size_t total = 0; while (total < IPC_HEADER_SIZE) { @@ -93,15 +87,15 @@ struct ipc_response *ipc_recv_response(int socketfd) { goto error_1; } - memcpy(&response->size, data + sizeof(ipc_magic), sizeof(uint32_t)); - memcpy(&response->type, data + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t)); + total = 0; + memcpy(&response->size, &data32[0], sizeof(data32[0])); + memcpy(&response->type, &data32[1], sizeof(data32[1])); char *payload = malloc(response->size + 1); if (!payload) { goto error_2; } - total = 0; while (total < response->size) { ssize_t received = recv(socketfd, payload + total, response->size - total, 0); if (received < 0) { @@ -115,6 +109,7 @@ struct ipc_response *ipc_recv_response(int socketfd) { return response; error_2: free(response); + free(payload); error_1: sway_log(SWAY_ERROR, "Unable to allocate memory for IPC response"); return NULL; @@ -127,9 +122,10 @@ void free_ipc_response(struct ipc_response *response) { char *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) { char data[IPC_HEADER_SIZE]; + uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic)); memcpy(data, ipc_magic, sizeof(ipc_magic)); - memcpy(data + sizeof(ipc_magic), len, sizeof(*len)); - memcpy(data + sizeof(ipc_magic) + sizeof(*len), &type, sizeof(type)); + memcpy(&data32[0], len, sizeof(*len)); + memcpy(&data32[1], &type, sizeof(type)); if (write(socketfd, data, IPC_HEADER_SIZE) == -1) { sway_abort("Unable to send IPC header"); diff --git a/common/log.c b/common/log.c index 3eacdb346..669fd360d 100644 --- a/common/log.c +++ b/common/log.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 199506L #include #include #include @@ -35,63 +36,34 @@ bool _sway_assert(bool condition, const char *format, ...) { static bool colored = true; static sway_log_importance_t log_importance = SWAY_ERROR; -static struct timespec start_time = {-1, -1}; static const char *verbosity_colors[] = { [SWAY_SILENT] = "", [SWAY_ERROR ] = "\x1B[1;31m", [SWAY_INFO ] = "\x1B[1;34m", - [SWAY_DEBUG ] = "\x1B[1;90m", + [SWAY_DEBUG ] = "\x1B[1;30m", }; -static const char *verbosity_headers[] = { - [SWAY_SILENT] = "", - [SWAY_ERROR] = "[ERROR]", - [SWAY_INFO] = "[INFO]", - [SWAY_DEBUG] = "[DEBUG]", -}; - -static void timespec_sub(struct timespec *r, const struct timespec *a, - const struct timespec *b) { - const long NSEC_PER_SEC = 1000000000; - r->tv_sec = a->tv_sec - b->tv_sec; - r->tv_nsec = a->tv_nsec - b->tv_nsec; - if (r->tv_nsec < 0) { - r->tv_sec--; - r->tv_nsec += NSEC_PER_SEC; - } -} - -static void init_start_time(void) { - if (start_time.tv_sec >= 0) { - return; - } - clock_gettime(CLOCK_MONOTONIC, &start_time); -} - static void sway_log_stderr(sway_log_importance_t verbosity, const char *fmt, va_list args) { - init_start_time(); - if (verbosity > log_importance) { return; } + // prefix the time to the log message + struct tm result; + time_t t = time(NULL); + struct tm *tm_info = localtime_r(&t, &result); + char buffer[26]; - struct timespec ts = {0}; - clock_gettime(CLOCK_MONOTONIC, &ts); - timespec_sub(&ts, &ts, &start_time); - - fprintf(stderr, "%02d:%02d:%02d.%03ld ", (int)(ts.tv_sec / 60 / 60), - (int)(ts.tv_sec / 60 % 60), (int)(ts.tv_sec % 60), - ts.tv_nsec / 1000000); + // generate time prefix + strftime(buffer, sizeof(buffer), "%F %T - ", tm_info); + fprintf(stderr, "%s", buffer); unsigned c = (verbosity < SWAY_LOG_IMPORTANCE_LAST) ? verbosity : SWAY_LOG_IMPORTANCE_LAST - 1; if (colored && isatty(STDERR_FILENO)) { fprintf(stderr, "%s", verbosity_colors[c]); - } else { - fprintf(stderr, "%s ", verbosity_headers[c]); } vfprintf(stderr, fmt, args); @@ -103,8 +75,6 @@ static void sway_log_stderr(sway_log_importance_t verbosity, const char *fmt, } void sway_log_init(sway_log_importance_t verbosity, terminate_callback_t callback) { - init_start_time(); - if (verbosity < SWAY_LOG_IMPORTANCE_LAST) { log_importance = verbosity; } @@ -123,3 +93,20 @@ void _sway_log(sway_log_importance_t verbosity, const char *fmt, ...) { sway_log_stderr(verbosity, fmt, args); va_end(args); } + +// strips the path prefix from filepath +// will try to strip SWAY_SRC_DIR as well as a relative src dir +// e.g. '/src/build/sway/util/log.c' and +// '../util/log.c' will both be stripped to +// 'util/log.c' +const char *_sway_strip_path(const char *filepath) { + static int srclen = sizeof(SWAY_SRC_DIR); + if (strstr(filepath, SWAY_SRC_DIR) == filepath) { + filepath += srclen; + } else if (*filepath == '.') { + while (*filepath == '.' || *filepath == '/') { + ++filepath; + } + } + return filepath; +} diff --git a/common/loop.c b/common/loop.c index b99c6d55c..aecad2f3f 100644 --- a/common/loop.c +++ b/common/loop.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200112L #include #include #include @@ -116,15 +117,9 @@ void loop_add_fd(struct loop *loop, int fd, short mask, struct pollfd pfd = {fd, mask, 0}; if (loop->fd_length == loop->fd_capacity) { - int capacity = loop->fd_capacity + 10; - struct pollfd *tmp = realloc(loop->fds, - sizeof(struct pollfd) * capacity); - if (!tmp) { - sway_log(SWAY_ERROR, "Unable to allocate memory for pollfd"); - return; - } - loop->fds = tmp; - loop->fd_capacity = capacity; + loop->fd_capacity += 10; + loop->fds = realloc(loop->fds, + sizeof(struct pollfd) * loop->fd_capacity); } loop->fds[loop->fd_length++] = pfd; diff --git a/common/meson.build b/common/meson.build index c0ce1f681..8561d6cf0 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,8 +1,8 @@ lib_sway_common = static_library( 'sway-common', files( + 'background-image.c', 'cairo.c', - 'gesture.c', 'ipc-client.c', 'log.c', 'loop.c', @@ -13,9 +13,9 @@ lib_sway_common = static_library( ), dependencies: [ cairo, + gdk_pixbuf, pango, - pangocairo, - wayland_client.partial_dependency(compile_args: true) + pangocairo ], include_directories: sway_inc ) diff --git a/common/pango.c b/common/pango.c index 781d7b312..fc3d06886 100644 --- a/common/pango.c +++ b/common/pango.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -6,7 +6,7 @@ #include #include #include -#include "cairo_util.h" +#include "cairo.h" #include "log.h" #include "stringop.h" @@ -50,11 +50,9 @@ size_t escape_markup_text(const char *src, char *dest) { return length; } -PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, double scale, bool markup) { PangoLayout *layout = pango_cairo_create_layout(cairo); - pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); - PangoAttrList *attrs; if (markup) { char *buf; @@ -75,95 +73,66 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, } pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); + PangoFontDescription *desc = pango_font_description_from_string(font); pango_layout_set_font_description(layout, desc); pango_layout_set_single_paragraph_mode(layout, 1); pango_layout_set_attributes(layout, attrs); pango_attr_list_unref(attrs); + pango_font_description_free(desc); return layout; } -void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, +void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, int *baseline, double scale, bool markup, const char *fmt, ...) { - if (width) { - *width = 0; - } - if (height) { - *height = 0; - } - if (baseline) { - *baseline = 0; - } - va_list args; va_start(args, fmt); - char *buf = vformat_str(fmt, args); + // Add one since vsnprintf excludes null terminator. + int length = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); + + char *buf = malloc(length); if (buf == NULL) { - sway_log(SWAY_ERROR, "Failed to format string"); + sway_log(SWAY_ERROR, "Failed to allocate memory"); return; } + va_start(args, fmt); + vsnprintf(buf, length, fmt, args); + va_end(args); - PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); + PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); pango_cairo_update_layout(cairo, layout); - cairo_status_t status = cairo_status(cairo); - if (status != CAIRO_STATUS_SUCCESS) { - sway_log(SWAY_ERROR, "pango_cairo_update_layout() failed: %s", - cairo_status_to_string(status)); - goto out; - } - pango_layout_get_pixel_size(layout, width, height); if (baseline) { *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; } - -out: g_object_unref(layout); free(buf); } -void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) { - PangoFontMap *fontmap = pango_cairo_font_map_get_default(); - PangoContext *pango = pango_font_map_create_context(fontmap); - pango_context_set_round_glyph_positions(pango, false); - // When passing NULL as a language, pango uses the current locale. - PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); - - *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; - *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE; - - pango_font_metrics_unref(metrics); - g_object_unref(pango); -} - -void render_text(cairo_t *cairo, const PangoFontDescription *desc, +void pango_printf(cairo_t *cairo, const char *font, double scale, bool markup, const char *fmt, ...) { va_list args; va_start(args, fmt); - char *buf = vformat_str(fmt, args); + // Add one since vsnprintf excludes null terminator. + int length = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); + + char *buf = malloc(length); if (buf == NULL) { - sway_log(SWAY_ERROR, "Failed to format string"); + sway_log(SWAY_ERROR, "Failed to allocate memory"); return; } + va_start(args, fmt); + vsnprintf(buf, length, fmt, args); + va_end(args); - PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); + PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); cairo_font_options_t *fo = cairo_font_options_create(); cairo_get_font_options(cairo, fo); pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); cairo_font_options_destroy(fo); - pango_cairo_update_layout(cairo, layout); - cairo_status_t status = cairo_status(cairo); - if (status != CAIRO_STATUS_SUCCESS) { - sway_log(SWAY_ERROR, "pango_cairo_update_layout() failed: %s", - cairo_status_to_string(status)); - goto out; - } - pango_cairo_show_layout(cairo, layout); - -out: g_object_unref(layout); free(buf); } diff --git a/common/stringop.c b/common/stringop.c index dcbd16564..709be6842 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,14 +1,13 @@ -#include -#include -#include -#include +#define _POSIX_C_SOURCE 200809L #include +#include #include #include -#include -#include "list.h" -#include "log.h" +#include #include "stringop.h" +#include "log.h" +#include "string.h" +#include "list.h" static const char whitespace[] = " \f\n\r\t\v"; @@ -64,7 +63,7 @@ char *lenient_strncat(char *dest, const char *src, size_t len) { } // strcmp that also handles null pointers. -int lenient_strcmp(const char *a, const char *b) { +int lenient_strcmp(char *a, char *b) { if (a == b) { return 0; } else if (!a) { @@ -79,10 +78,12 @@ int lenient_strcmp(const char *a, const char *b) { list_t *split_string(const char *str, const char *delims) { list_t *res = create_list(); char *copy = strdup(str); + char *token; - char *token = strtok(copy, delims); - while (token) { - list_add(res, strdup(token)); + token = strtok(copy, delims); + while(token) { + token = strdup(token); + list_add(res, token); token = strtok(NULL, delims); } free(copy); @@ -96,7 +97,7 @@ char **split_args(const char *start, int *argc) { bool in_token = false; bool in_string = false; bool in_char = false; - bool in_brackets = false; // brackets are used for criteria + bool in_brackets = false; // brackets are used for critera bool escaped = false; const char *end = start; if (start) { @@ -149,6 +150,29 @@ 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); @@ -252,115 +276,130 @@ char *join_args(char **argv, int argc) { return res; } -static inline char *argsep_next_interesting(const char *src, const char *delim) { - char *special = strpbrk(src, "\"'\\"); - char *next_delim = strpbrk(src, delim); - if (!special) { - return next_delim; +static bool has_whitespace(const char *str) { + while (*str) { + if (isspace(*str)) { + return true; + } + ++str; } - if (!next_delim) { - return special; - } - return (next_delim < special) ? next_delim : special; + return false; } -char *argsep(char **stringp, const char *delim, char *matched) { +/** + * 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; bool in_string = false; bool in_char = false; bool escaped = false; - char *interesting = NULL; - - while ((interesting = argsep_next_interesting(end, delim))) { - if (escaped && interesting != end) { - escaped = false; - } - if (*interesting == '"' && !in_char && !escaped) { + while (1) { + if (*end == '"' && !in_char && !escaped) { in_string = !in_string; - end = interesting + 1; - } else if (*interesting == '\'' && !in_string && !escaped) { + } else if (*end == '\'' && !in_string && !escaped) { in_char = !in_char; - end = interesting + 1; - } else if (*interesting == '\\') { + } else if (*end == '\\') { escaped = !escaped; - end = interesting + 1; - } else if (!in_string && !in_char && !escaped) { - // We must have matched a separator - end = interesting; - if (matched) { - *matched = *end; - } + } else if (*end == '\0') { + *stringp = NULL; + goto found; + } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { if (end - start) { *(end++) = 0; - *stringp = end; - break; + *stringp = end + strspn(end, delim);; + if (!**stringp) *stringp = NULL; + goto found; } else { - end = ++start; + ++start; + end = start; } - } else { - end++; } - } - if (!interesting) { - *stringp = NULL; - if (matched) { - *matched = '\0'; + if (*end != '\\') { + escaped = false; } + ++end; } + found: return start; } -bool expand_path(char **path) { - wordexp_t p = {0}; - while (strstr(*path, " ")) { - *path = realloc(*path, strlen(*path) + 2); - char *ptr = strstr(*path, " ") + 1; - memmove(ptr + 1, ptr, strlen(ptr) + 1); - *ptr = '\\'; +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; } - if (wordexp(*path, &p, 0) != 0 || p.we_wordv[0] == NULL) { - wordfree(&p); - return false; - } - free(*path); - *path = join_args(p.we_wordv, p.we_wordc); - wordfree(&p); - return true; -} - -char *vformat_str(const char *fmt, va_list args) { - char *str = NULL; - va_list args_copy; - va_copy(args_copy, args); - - int len = vsnprintf(NULL, 0, fmt, args); - if (len < 0) { - sway_log_errno(SWAY_ERROR, "vsnprintf(\"%s\") failed", fmt); - goto out; - } - - str = malloc(len + 1); - if (str == NULL) { - sway_log_errno(SWAY_ERROR, "malloc() failed"); - goto out; - } - - vsnprintf(str, len + 1, fmt, args_copy); - -out: - va_end(args_copy); - return str; -} - -char *format_str(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - char *str = vformat_str(fmt, args); - va_end(args); - return str; -} - -bool has_prefix(const char *str, const char *prefix) { - return strncmp(str, prefix, strlen(prefix)) == 0; + return NULL; } diff --git a/common/util.c b/common/util.c index ae1fe4431..edbbf3f77 100644 --- a/common/util.c +++ b/common/util.c @@ -1,11 +1,9 @@ -#include -#include +#define _POSIX_C_SOURCE 200809L +#include #include -#include #include #include #include -#include #include "log.h" #include "util.h" @@ -13,28 +11,21 @@ int wrap(int i, int max) { return ((i % max) + max) % max; } -bool parse_color(const char *color, uint32_t *result) { +uint32_t parse_color(const char *color) { if (color[0] == '#') { ++color; } - int len = strlen(color); - if ((len != 6 && len != 8) || !isxdigit(color[0]) || !isxdigit(color[1])) { - return false; - } - char *ptr; - uint32_t parsed = strtoul(color, &ptr, 16); - if (*ptr != '\0') { - return false; - } - *result = len == 6 ? ((parsed << 8) | 0xFF) : parsed; - return true; -} -void color_to_rgba(float dest[static 4], uint32_t color) { - dest[0] = ((color >> 24) & 0xff) / 255.0; - dest[1] = ((color >> 16) & 0xff) / 255.0; - dest[2] = ((color >> 8) & 0xff) / 255.0; - dest[3] = (color & 0xff) / 255.0; + int len = strlen(color); + if (len != 6 && len != 8) { + sway_log(SWAY_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color); + return 0xFFFFFFFF; + } + uint32_t res = (uint32_t)strtoul(color, NULL, 16); + if (strlen(color) == 6) { + res = (res << 8) | 0xFF; + } + return res; } bool parse_boolean(const char *boolean, bool current) { @@ -63,87 +54,3 @@ float parse_float(const char *value) { } return flt; } - -enum movement_unit parse_movement_unit(const char *unit) { - if (strcasecmp(unit, "px") == 0) { - return MOVEMENT_UNIT_PX; - } - if (strcasecmp(unit, "ppt") == 0) { - return MOVEMENT_UNIT_PPT; - } - if (strcasecmp(unit, "default") == 0) { - return MOVEMENT_UNIT_DEFAULT; - } - return MOVEMENT_UNIT_INVALID; -} - -int parse_movement_amount(int argc, char **argv, - struct movement_amount *amount) { - if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) { - amount->amount = 0; - amount->unit = MOVEMENT_UNIT_INVALID; - return 0; - } - - char *err; - amount->amount = (int)strtol(argv[0], &err, 10); - if (*err) { - // e.g. 10px - amount->unit = parse_movement_unit(err); - return 1; - } - if (argc == 1) { - amount->unit = MOVEMENT_UNIT_DEFAULT; - return 1; - } - // Try the second argument - amount->unit = parse_movement_unit(argv[1]); - if (amount->unit == MOVEMENT_UNIT_INVALID) { - amount->unit = MOVEMENT_UNIT_DEFAULT; - return 1; - } - return 2; -} - -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 sway_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; -} - -uint32_t get_current_time_in_msec(void) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - return now.tv_sec * 1000 + now.tv_nsec / 1000000; -} diff --git a/completions/bash/sway b/completions/bash/sway index 01b20073f..edd752cdb 100644 --- a/completions/bash/sway +++ b/completions/bash/sway @@ -2,7 +2,7 @@ _sway() { - local cur prev short long + local cur prev _get_comp_words_by_ref cur prev short=( diff --git a/completions/bash/swaybar b/completions/bash/swaybar index 3709d4f97..1e085c65e 100644 --- a/completions/bash/swaybar +++ b/completions/bash/swaybar @@ -2,7 +2,7 @@ _swaybar() { - local cur prev short long + local cur prev _get_comp_words_by_ref cur prev short=( diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index 304577516..20092bdc4 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -2,7 +2,7 @@ _swaymsg() { - local cur prev types short long + local cur prev _get_comp_words_by_ref cur prev types=( @@ -15,16 +15,12 @@ _swaymsg() 'get_bar_config' 'get_version' 'get_binding_modes' - 'get_binding_state' 'get_config' 'send_tick' - 'subscribe' ) short=( -h - -m - -p -q -r -s @@ -34,13 +30,11 @@ _swaymsg() long=( --help - --monitor - --pretty --quiet --raw --socket --type - --version + --verbose ) case $prev in diff --git a/completions/fish/sway.fish b/completions/fish/sway.fish index 444fd273d..31165ef4f 100644 --- a/completions/fish/sway.fish +++ b/completions/fish/sway.fish @@ -1,8 +1,7 @@ # 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." -r +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." complete -c sway -s d -l debug --description "Enables full logging, including debug information." complete -c sway -s v -l version --description "Show the version number and quit." diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index 5ee7dcc0b..fd577413f 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -1,13 +1,10 @@ # 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,8 +15,6 @@ complete -c swaymsg -s t -l type -fra 'get_marks' --description "Get a JSON-enco complete -c swaymsg -s t -l type -fra 'get_bar_config' --description "Get a JSON-encoded configuration for swaybar." complete -c swaymsg -s t -l type -fra 'get_version' --description "Get JSON-encoded version information for the running instance of sway." complete -c swaymsg -s t -l type -fra 'get_binding_modes' --description "Gets a JSON-encoded list of currently configured binding modes." -complete -c swaymsg -s t -l type -fra 'get_binding_state' --description "Get JSON-encoded info about the current binding state." 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 7015d5e34..784d7fade 100644 --- a/completions/fish/swaynag.fish +++ b/completions/fish/swaynag.fish @@ -1,30 +1,29 @@ # swaynag -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 C -l config --description 'The config file to use. Default: $HOME/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config, and SYSCONFDIR/swaylock/config.' complete -c swaynag -s d -l debug --description 'Enable debugging.' -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 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 h -l help --description 'Show help message and quit.' -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 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 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 -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 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 v -l version --description 'Show the version number and quit.' # Appearance -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.' +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.' diff --git a/completions/meson.build b/completions/meson.build deleted file mode 100644 index 6bca93913..000000000 --- a/completions/meson.build +++ /dev/null @@ -1,57 +0,0 @@ -if get_option('zsh-completions') - zsh_files = files( - 'zsh/_sway', - 'zsh/_swaymsg', - ) - zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') - - install_data(zsh_files, install_dir: zsh_install_dir) -endif - -if get_option('bash-completions') - bash_comp = dependency('bash-completion', required: false) - - bash_files = files( - 'bash/sway', - 'bash/swaymsg', - ) - - if get_option('swaybar') - bash_files += files('bash/swaybar') - endif - - if bash_comp.found() - bash_install_dir = bash_comp.get_variable( - pkgconfig: 'completionsdir', - pkgconfig_define: ['datadir', datadir] - ) - else - bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') - endif - - install_data(bash_files, install_dir: bash_install_dir) -endif - -if get_option('fish-completions') - fish_comp = dependency('fish', required: false) - - fish_files = files( - 'fish/sway.fish', - 'fish/swaymsg.fish', - ) - - if get_option('swaynag') - fish_files += files('fish/swaynag.fish') - endif - - if fish_comp.found() - fish_install_dir = fish_comp.get_variable( - pkgconfig: 'completionsdir', - pkgconfig_define: ['datadir', datadir] - ) - else - fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') - endif - - install_data(fish_files, install_dir: fish_install_dir) -endif diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 106f3d9dc..0ba45d4a9 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -23,18 +23,15 @@ types=( 'get_bar_config' 'get_version' 'get_binding_modes' -'get_binding_state' 'get_config' 'send_tick' -'subscribe' ) _arguments -s \ - '(-h --help)'{-h,--help}'[Show help message and quit]' \ + '(-v --version)'{-v,--version}'[Show the version number 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]' \ + '(-h --help)'{-h,--help}'[Show help message and quit]' \ '(-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}' \ - '(-v --version)'{-v,--version}'[Show the version number and quit]' + '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}' diff --git a/config.in b/config.in index d71bc628b..fbd952353 100644 --- a/config.in +++ b/config.in @@ -14,9 +14,10 @@ set $down j set $up k set $right l # Your preferred terminal emulator -set $term foot +set $term urxvt # Your preferred application launcher -set $menu wmenu-run +# Note: it's recommended that you pass the final command to sway +set $menu dmenu_path | dmenu | xargs swaymsg exec ### Output configuration # @@ -35,7 +36,8 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # exec swayidle -w \ # timeout 300 'swaylock -f -c 000000' \ -# timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \ +# timeout 600 'swaymsg "output * dpms off"' \ +# resume 'swaymsg "output * dpms on"' \ # before-sleep 'swaylock -f -c 000000' # # This will lock your screen after 300 seconds of inactivity, then turn off @@ -46,31 +48,27 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # Example configuration: # -# input type:touchpad { +# input "2:14:SynPS/2_Synaptics_TouchPad" { # dwt enabled # tap enabled # natural_scroll enabled # middle_emulation enabled # } # -# input type:keyboard { -# xkb_layout "eu" -# } -# -# You can also configure each device individually. +# You can get the names of your inputs by running: swaymsg -t get_inputs # Read `man 5 sway-input` for more information about this section. ### Key bindings # # 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. @@ -80,11 +78,11 @@ 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) - 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' + # 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: # @@ -93,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 @@ -112,28 +110,28 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # Workspaces: # - # Switch to workspace - bindsym $mod+1 workspace number 1 - bindsym $mod+2 workspace number 2 - bindsym $mod+3 workspace number 3 - bindsym $mod+4 workspace number 4 - bindsym $mod+5 workspace number 5 - bindsym $mod+6 workspace number 6 - bindsym $mod+7 workspace number 7 - bindsym $mod+8 workspace number 8 - bindsym $mod+9 workspace number 9 - bindsym $mod+0 workspace number 10 - # Move focused container to workspace - bindsym $mod+Shift+1 move container to workspace number 1 - bindsym $mod+Shift+2 move container to workspace number 2 - bindsym $mod+Shift+3 move container to workspace number 3 - bindsym $mod+Shift+4 move container to workspace number 4 - bindsym $mod+Shift+5 move container to workspace number 5 - bindsym $mod+Shift+6 move container to workspace number 6 - bindsym $mod+Shift+7 move container to workspace number 7 - bindsym $mod+Shift+8 move container to workspace number 8 - bindsym $mod+Shift+9 move container to workspace number 9 - bindsym $mod+Shift+0 move container to workspace number 10 + # switch to workspace + bindsym $mod+1 workspace 1 + bindsym $mod+2 workspace 2 + bindsym $mod+3 workspace 3 + bindsym $mod+4 workspace 4 + bindsym $mod+5 workspace 5 + bindsym $mod+6 workspace 6 + bindsym $mod+7 workspace 7 + bindsym $mod+8 workspace 8 + bindsym $mod+9 workspace 9 + bindsym $mod+0 workspace 10 + # 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 + bindsym $mod+Shift+4 move container to workspace 4 + bindsym $mod+Shift+5 move container to workspace 5 + bindsym $mod+Shift+6 move container to workspace 6 + bindsym $mod+Shift+7 move container to workspace 7 + bindsym $mod+Shift+8 move container to workspace 8 + bindsym $mod+Shift+9 move container to workspace 9 + bindsym $mod+Shift+0 move container to workspace 10 # Note: workspaces can have any name you want, not just numbers. # We just use 1-10 as the default. # @@ -159,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: @@ -186,39 +184,17 @@ 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" } bindsym $mod+r mode "resize" -# -# Utilities: -# - # Special keys to adjust volume via PulseAudio - bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle - bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5% - bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5% - bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle - - # Special keys to control media via playerctl - bindsym --locked XF86AudioPlay exec playerctl play-pause - bindsym --locked XF86AudioPause exec playerctl play-pause - bindsym --locked XF86AudioPrev exec playerctl previous - bindsym --locked XF86AudioNext exec playerctl next - bindsym --locked XF86AudioStop exec playerctl stop - - # Special keys to adjust brightness via brightnessctl - bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%- - bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+ - - # Special key to take a screenshot with grim - bindsym Print exec grim # # Status Bar: @@ -229,7 +205,7 @@ bar { # When the status_command prints a new line to stdout, swaybar updates. # The default just shows the current date and time. - status_command while date +'%Y-%m-%d %X'; do sleep 1; done + status_command while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done colors { statusline #ffffff diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py new file mode 100755 index 000000000..af26c0176 --- /dev/null +++ b/contrib/inactive-windows-transparency.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +# This script requires i3ipc-python package (install it from a system package manager +# or pip). +# It makes inactive windows transparent. Use `transparency_val` variable to control +# transparency strength in range of 0…1. + +import i3ipc + +transparency_val = '0.8'; +ipc = i3ipc.Connection() +prev_focused = None + +for window in ipc.get_tree(): + if window.focused: + prev_focused = window + else: + window.command('opacity ' + transparency_val) + +def on_window_focus(ipc, focused): + global prev_focused + if focused.container.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859 + focused.container.command('opacity 1') + prev_focused.command('opacity ' + transparency_val) + prev_focused = focused.container + +ipc.on("window::focus", on_window_focus) +ipc.main() diff --git a/include/background-image.h b/include/background-image.h new file mode 100644 index 000000000..15935ffd3 --- /dev/null +++ b/include/background-image.h @@ -0,0 +1,20 @@ +#ifndef _SWAY_BACKGROUND_IMAGE_H +#define _SWAY_BACKGROUND_IMAGE_H +#include "cairo.h" + +enum background_mode { + BACKGROUND_MODE_STRETCH, + BACKGROUND_MODE_FILL, + BACKGROUND_MODE_FIT, + BACKGROUND_MODE_CENTER, + BACKGROUND_MODE_TILE, + BACKGROUND_MODE_SOLID_COLOR, + BACKGROUND_MODE_INVALID, +}; + +enum background_mode parse_background_mode(const char *mode); +cairo_surface_t *load_background_image(const char *path); +void render_background_image(cairo_t *cairo, cairo_surface_t *image, + enum background_mode mode, int buffer_width, int buffer_height); + +#endif diff --git a/include/cairo_util.h b/include/cairo.h similarity index 54% rename from include/cairo_util.h rename to include/cairo.h index dc049c6dc..d1b9b8d78 100644 --- a/include/cairo_util.h +++ b/include/cairo.h @@ -1,9 +1,13 @@ -#ifndef _SWAY_CAIRO_UTIL_H -#define _SWAY_CAIRO_UTIL_H +#ifndef _SWAY_CAIRO_H +#define _SWAY_CAIRO_H + #include "config.h" #include -#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); @@ -11,4 +15,11 @@ 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/gesture.h b/include/gesture.h deleted file mode 100644 index 9c6b0f91c..000000000 --- a/include/gesture.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef _SWAY_GESTURE_H -#define _SWAY_GESTURE_H - -#include -#include - -/** - * A gesture type used in binding. - */ -enum gesture_type { - GESTURE_TYPE_NONE = 0, - GESTURE_TYPE_HOLD, - GESTURE_TYPE_PINCH, - GESTURE_TYPE_SWIPE, -}; - -// Turns single type enum value to constant string representation. -const char *gesture_type_string(enum gesture_type direction); - -// Value to use to accept any finger count -extern const uint8_t GESTURE_FINGERS_ANY; - -/** - * A gesture direction used in binding. - */ -enum gesture_direction { - GESTURE_DIRECTION_NONE = 0, - // Directions based on delta x and y - GESTURE_DIRECTION_UP = 1 << 0, - GESTURE_DIRECTION_DOWN = 1 << 1, - GESTURE_DIRECTION_LEFT = 1 << 2, - GESTURE_DIRECTION_RIGHT = 1 << 3, - // Directions based on scale - GESTURE_DIRECTION_INWARD = 1 << 4, - GESTURE_DIRECTION_OUTWARD = 1 << 5, - // Directions based on rotation - GESTURE_DIRECTION_CLOCKWISE = 1 << 6, - GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7, -}; - -// Turns single direction enum value to constant string representation. -const char *gesture_direction_string(enum gesture_direction direction); - -/** - * Struct representing a pointer gesture - */ -struct gesture { - enum gesture_type type; - uint8_t fingers; - uint32_t directions; -}; - -/** - * Parses gesture from [:][:] string. - * - * Return NULL on success, otherwise error message string - */ -char *gesture_parse(const char *input, struct gesture *output); - -// Turns gesture into string representation -char *gesture_to_string(struct gesture *gesture); - -// Check if gesture is of certain type and finger count. -bool gesture_check(struct gesture *target, - enum gesture_type type, uint8_t fingers); - -// Check if a gesture target/binding is match by other gesture/input -bool gesture_match(struct gesture *target, - struct gesture *to_match, bool exact); - -// Returns true if gesture are exactly the same -bool gesture_equal(struct gesture *a, struct gesture *b); - -// Compare distance between two matched target gestures. -int8_t gesture_compare(struct gesture *a, struct gesture *b); - -// Small helper struct to track gestures over time -struct gesture_tracker { - enum gesture_type type; - uint8_t fingers; - double dx, dy; - double scale; - double rotation; -}; - -// Begin gesture tracking -void gesture_tracker_begin(struct gesture_tracker *tracker, - enum gesture_type type, uint8_t fingers); - -// Check if the provides type is currently being tracked -bool gesture_tracker_check(struct gesture_tracker *tracker, - enum gesture_type type); - -// Update gesture track with new data point -void gesture_tracker_update(struct gesture_tracker *tracker, double dx, - double dy, double scale, double rotation); - -// Reset tracker -void gesture_tracker_cancel(struct gesture_tracker *tracker); - -// Reset tracker and return gesture tracked -struct gesture *gesture_tracker_end(struct gesture_tracker *tracker); - -#endif diff --git a/include/ipc-client.h b/include/ipc-client.h index 9c5712d73..c9f5b3441 100644 --- a/include/ipc-client.h +++ b/include/ipc-client.h @@ -1,12 +1,7 @@ #ifndef _SWAY_IPC_CLIENT_H #define _SWAY_IPC_CLIENT_H -// arbitrary number, it's probably sufficient, higher number = more memory usage -#define JSON_MAX_DEPTH 512 - -#include #include -#include #include "ipc.h" @@ -41,9 +36,5 @@ 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/ipc.h b/include/ipc.h index ff0117502..6063f69c5 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -17,7 +17,6 @@ enum ipc_command_type { IPC_GET_CONFIG = 9, IPC_SEND_TICK = 10, IPC_SYNC = 11, - IPC_GET_BINDING_STATE = 12, // sway-specific command types IPC_GET_INPUTS = 100, @@ -35,7 +34,6 @@ enum ipc_command_type { // sway-specific event types IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20), - IPC_EVENT_INPUT = ((1<<31) | 21), }; #endif diff --git a/include/log.h b/include/log.h index 41399a9fa..adf431ab6 100644 --- a/include/log.h +++ b/include/log.h @@ -33,26 +33,22 @@ void _sway_vlog(sway_log_importance_t verbosity, const char *format, va_list arg void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2); bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3); -#ifdef SWAY_REL_SRC_DIR -// strip prefix from __FILE__, leaving the path relative to the project root -#define _SWAY_FILENAME ((const char *)__FILE__ + sizeof(SWAY_REL_SRC_DIR) - 1) -#else -#define _SWAY_FILENAME __FILE__ -#endif +// TODO: get meson to precompute this, for better reproducibility/less overhead +const char *_sway_strip_path(const char *filepath); #define sway_log(verb, fmt, ...) \ - _sway_log(verb, "[%s:%d] " fmt, _SWAY_FILENAME, __LINE__, ##__VA_ARGS__) + _sway_log(verb, "[%s:%d] " fmt, _sway_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) #define sway_vlog(verb, fmt, args) \ - _sway_vlog(verb, "[%s:%d] " fmt, _SWAY_FILENAME, __LINE__, args) + _sway_vlog(verb, "[%s:%d] " fmt, _sway_strip_path(__FILE__), __LINE__, args) #define sway_log_errno(verb, fmt, ...) \ sway_log(verb, fmt ": %s", ##__VA_ARGS__, strerror(errno)) #define sway_abort(FMT, ...) \ - _sway_abort("[%s:%d] " FMT, _SWAY_FILENAME, __LINE__, ##__VA_ARGS__) + _sway_abort("[%s:%d] " FMT, _sway_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) #define sway_assert(COND, FMT, ...) \ - _sway_assert(COND, "[%s:%d] %s:" FMT, _SWAY_FILENAME, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) + _sway_assert(COND, "[%s:%d] %s:" FMT, _sway_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) #endif diff --git a/include/pango.h b/include/pango.h index 228e39cfa..6ab83c167 100644 --- a/include/pango.h +++ b/include/pango.h @@ -3,9 +3,8 @@ #include #include #include -#include +#include #include -#include "stringop.h" /** * Utility function which escape characters a & < > ' ". @@ -14,12 +13,11 @@ * escaped string to dest if provided. */ size_t escape_markup_text(const char *src, char *dest); -PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, double scale, bool markup); -void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, - int *baseline, double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(8, 9); -void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); -void render_text(cairo_t *cairo, PangoFontDescription *desc, - double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6); +void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, + int *baseline, double scale, bool markup, const char *fmt, ...); +void pango_printf(cairo_t *cairo, const char *font, + double scale, bool markup, const char *fmt, ...); #endif diff --git a/include/pool-buffer.h b/include/pool-buffer.h index b7a95afe9..54f5be069 100644 --- a/include/pool-buffer.h +++ b/include/pool-buffer.h @@ -1,6 +1,6 @@ #ifndef _SWAY_BUFFERS_H #define _SWAY_BUFFERS_H -#include +#include #include #include #include diff --git a/include/stringop.h b/include/stringop.h index ffc355cf0..f7ca60a51 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -1,17 +1,10 @@ #ifndef _SWAY_STRINGOP_H #define _SWAY_STRINGOP_H -#include -#include #include "list.h" -#ifdef __GNUC__ -#define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) -#else -#define _SWAY_ATTRIB_PRINTF(start, end) -#endif - 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 @@ -19,7 +12,7 @@ char *lenient_strcat(char *dest, const char *src); char *lenient_strncat(char *dest, const char *src, size_t len); // strcmp that also handles null pointers. -int lenient_strcmp(const char *a, const char *b); +int lenient_strcmp(char *a, char *b); // Simply split a string with delims, free with `list_free_items_and_destroy` list_t *split_string(const char *str, const char *delims); @@ -28,18 +21,22 @@ 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, char *matched_delim); +char *argsep(char **stringp, const char *delim); -// Expand a path using shell replacements such as $HOME and ~ -bool expand_path(char **path); - -char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0); -char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); - -bool has_prefix(const char *str, const char *prefix); +const char *strcasestr(const char *haystack, const char *needle); #endif diff --git a/include/sway/commands.h b/include/sway/commands.h index 389c382eb..764821a0e 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -3,14 +3,13 @@ #include #include "config.h" -#include "stringop.h" struct sway_container; typedef struct cmd_results *sway_cmd(int argc, char **argv); struct cmd_handler { - const char *command; + char *command; sway_cmd *handle; }; @@ -47,9 +46,8 @@ enum expected_args { struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val); -const struct cmd_handler *find_handler(const char *line, - const struct cmd_handler *cmd_handlers, size_t handlers_size); - +struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, + int handlers_size); /** * Parse and executes a command. * @@ -69,7 +67,7 @@ struct cmd_results *config_command(char *command, char **new_block); * Parse and handle a sub command */ struct cmd_results *config_subcommand(char **argv, int argc, - const struct cmd_handler *handlers, size_t handlers_size); + struct cmd_handler *handlers, size_t handlers_size); /* * Parses a command policy rule. */ @@ -77,7 +75,7 @@ struct cmd_results *config_commands_command(char *exec); /** * Allocates a cmd_results object. */ -struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...) _SWAY_ATTRIB_PRINTF(2, 3); +struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...); /** * Frees a cmd_results object. */ @@ -89,6 +87,8 @@ void free_cmd_results(struct cmd_results *results); */ char *cmd_results_to_json(list_t *res_list); +struct cmd_results *add_color(char *buffer, const char *color); + /** * TODO: Move this function and its dependent functions to container.c. */ @@ -98,24 +98,14 @@ void container_resize_tiled(struct sway_container *parent, uint32_t axis, struct sway_container *container_find_resize_parent(struct sway_container *con, uint32_t edge); -/** - * Handlers shared by exec and exec_always. - */ -sway_cmd cmd_exec_validate; -sway_cmd cmd_exec_process; - -sway_cmd cmd_allow_tearing; sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; -sway_cmd cmd_bindgesture; -sway_cmd cmd_bindswitch; sway_cmd cmd_bindsym; sway_cmd cmd_border; sway_cmd cmd_client_noop; sway_cmd cmd_client_focused; sway_cmd cmd_client_focused_inactive; -sway_cmd cmd_client_focused_tab_title; sway_cmd cmd_client_unfocused; sway_cmd cmd_client_urgent; sway_cmd cmd_client_placeholder; @@ -145,7 +135,6 @@ 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; @@ -153,19 +142,17 @@ sway_cmd cmd_kill; sway_cmd cmd_layout; sway_cmd cmd_log_colors; sway_cmd cmd_mark; -sway_cmd cmd_max_render_time; 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; +sway_cmd cmd_new_window; sway_cmd cmd_no_focus; sway_cmd cmd_output; sway_cmd cmd_permit; sway_cmd cmd_popup_during_fullscreen; -sway_cmd cmd_primary_selection; sway_cmd cmd_reject; sway_cmd cmd_reload; sway_cmd cmd_rename; @@ -173,7 +160,6 @@ sway_cmd cmd_resize; sway_cmd cmd_scratchpad; sway_cmd cmd_seamless_mouse; sway_cmd cmd_set; -sway_cmd cmd_shortcuts_inhibitor; sway_cmd cmd_show_marks; sway_cmd cmd_smart_borders; sway_cmd cmd_smart_gaps; @@ -191,10 +177,6 @@ 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_unbindgesture; -sway_cmd cmd_unbindsym; sway_cmd cmd_unmark; sway_cmd cmd_urgent; sway_cmd cmd_workspace; @@ -228,11 +210,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; -sway_cmd bar_cmd_workspace_min_width; sway_cmd bar_colors_cmd_active_workspace; sway_cmd bar_colors_cmd_background; @@ -248,72 +227,46 @@ sway_cmd bar_colors_cmd_urgent_workspace; sway_cmd input_cmd_seat; sway_cmd input_cmd_accel_profile; -sway_cmd input_cmd_calibration_matrix; sway_cmd input_cmd_click_method; -sway_cmd input_cmd_clickfinger_button_map; sway_cmd input_cmd_drag; sway_cmd input_cmd_drag_lock; sway_cmd input_cmd_dwt; -sway_cmd input_cmd_dwtp; sway_cmd input_cmd_events; sway_cmd input_cmd_left_handed; sway_cmd input_cmd_map_from_region; sway_cmd input_cmd_map_to_output; -sway_cmd input_cmd_map_to_region; sway_cmd input_cmd_middle_emulation; sway_cmd input_cmd_natural_scroll; sway_cmd input_cmd_pointer_accel; -sway_cmd input_cmd_rotation_angle; sway_cmd input_cmd_scroll_factor; sway_cmd input_cmd_repeat_delay; sway_cmd input_cmd_repeat_rate; sway_cmd input_cmd_scroll_button; -sway_cmd input_cmd_scroll_button_lock; sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; sway_cmd input_cmd_tap_button_map; -sway_cmd input_cmd_tool_mode; sway_cmd input_cmd_xkb_capslock; -sway_cmd input_cmd_xkb_file; sway_cmd input_cmd_xkb_layout; sway_cmd input_cmd_xkb_model; sway_cmd input_cmd_xkb_numlock; sway_cmd input_cmd_xkb_options; sway_cmd input_cmd_xkb_rules; -sway_cmd input_cmd_xkb_switch_layout; sway_cmd input_cmd_xkb_variant; -sway_cmd output_cmd_adaptive_sync; -sway_cmd output_cmd_allow_tearing; sway_cmd output_cmd_background; -sway_cmd output_cmd_color_profile; sway_cmd output_cmd_disable; sway_cmd output_cmd_dpms; sway_cmd output_cmd_enable; -sway_cmd output_cmd_hdr; -sway_cmd output_cmd_max_render_time; sway_cmd output_cmd_mode; -sway_cmd output_cmd_modeline; sway_cmd output_cmd_position; -sway_cmd output_cmd_power; -sway_cmd output_cmd_render_bit_depth; sway_cmd output_cmd_scale; -sway_cmd output_cmd_scale_filter; -sway_cmd output_cmd_subpixel; -sway_cmd output_cmd_toggle; sway_cmd output_cmd_transform; -sway_cmd output_cmd_unplug; sway_cmd seat_cmd_attach; sway_cmd seat_cmd_cursor; sway_cmd seat_cmd_fallback; sway_cmd seat_cmd_hide_cursor; -sway_cmd seat_cmd_idle_inhibit; -sway_cmd seat_cmd_idle_wake; -sway_cmd seat_cmd_keyboard_grouping; sway_cmd seat_cmd_pointer_constraint; -sway_cmd seat_cmd_shortcuts_inhibitor; -sway_cmd seat_cmd_xcursor_theme; sway_cmd cmd_ipc_cmd; sway_cmd cmd_ipc_events; diff --git a/include/sway/config.h b/include/sway/config.h index 16b822fea..54cdcc908 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -4,22 +4,14 @@ #include #include #include -#include -#include -#include -#include +#include #include -#include #include "../include/config.h" -#include "gesture.h" #include "list.h" -#include "stringop.h" #include "swaynag.h" #include "tree/container.h" -#include "sway/input/tablet.h" #include "sway/tree/root.h" #include "wlr-layer-shell-unstable-v1-protocol.h" -#include // TODO: Refactor this shit @@ -31,30 +23,24 @@ struct sway_variable { char *value; }; + enum binding_input_type { BINDING_KEYCODE, BINDING_KEYSYM, BINDING_MOUSECODE, BINDING_MOUSESYM, - BINDING_SWITCH, // dummy, only used to call seat_execute_command - BINDING_GESTURE // dummy, only used to call seat_execute_command }; enum binding_flags { - BINDING_RELEASE = 1 << 0, - BINDING_LOCKED = 1 << 1, // keyboard only - BINDING_BORDER = 1 << 2, // mouse only; trigger on container border - BINDING_CONTENTS = 1 << 3, // mouse only; trigger on container contents - BINDING_TITLEBAR = 1 << 4, // mouse only; trigger on container titlebar - BINDING_CODE = 1 << 5, // keyboard only; convert keysyms into keycodes - BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload - BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor - BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key - BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match + BINDING_RELEASE=1, + BINDING_LOCKED=2, // keyboard only + 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 }; /** - * A key (or mouse) binding and an associated command. + * A key binding and an associated command. */ struct sway_binding { enum binding_input_type type; @@ -62,35 +48,15 @@ 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; - xkb_layout_index_t group; - char *command; -}; - -enum sway_switch_trigger { - SWAY_SWITCH_TRIGGER_OFF, - SWAY_SWITCH_TRIGGER_ON, - SWAY_SWITCH_TRIGGER_TOGGLE, -}; - -/** - * A laptop switch binding and an associated command. - */ -struct sway_switch_binding { - enum wlr_switch_type type; - enum sway_switch_trigger trigger; - uint32_t flags; char *command; }; /** - * A gesture binding and an associated command. + * A mouse binding and an associated command. */ -struct sway_gesture_binding { - char *input; - uint32_t flags; - struct gesture gesture; +struct sway_mouse_binding { + uint32_t button; char *command; }; @@ -112,8 +78,6 @@ struct sway_mode { list_t *keysym_bindings; list_t *keycode_bindings; list_t *mouse_bindings; - list_t *switch_bindings; - list_t *gesture_bindings; bool pango; }; @@ -123,47 +87,25 @@ struct input_config_mapped_from_region { bool mm; }; -struct calibration_matrix { - bool configured; - float matrix[6]; -}; - -enum input_config_mapped_to { - MAPPED_TO_DEFAULT, - MAPPED_TO_OUTPUT, - MAPPED_TO_REGION, -}; - -struct input_config_tool { - enum wlr_tablet_tool_type type; - enum sway_tablet_tool_mode mode; -}; - /** * options for input devices */ struct input_config { char *identifier; - const char *input_type; int accel_profile; - struct calibration_matrix calibration_matrix; int click_method; - int clickfinger_button_map; int drag; int drag_lock; int dwt; - int dwtp; int left_handed; int middle_emulation; int natural_scroll; float pointer_accel; - float rotation_angle; float scroll_factor; int repeat_delay; int repeat_rate; int scroll_button; - int scroll_button_lock; int scroll_method; int send_events; int tap; @@ -174,20 +116,12 @@ struct input_config { char *xkb_options; char *xkb_rules; char *xkb_variant; - char *xkb_file; - - bool xkb_file_is_set; int xkb_numlock; int xkb_capslock; struct input_config_mapped_from_region *mapped_from_region; - - enum input_config_mapped_to mapped_to; char *mapped_to_output; - struct wlr_box *mapped_to_region; - - list_t *tools; bool capturable; struct wlr_box region; @@ -201,37 +135,10 @@ struct seat_attachment_config { // TODO other things are configured here for some reason }; -enum seat_config_hide_cursor_when_typing { - HIDE_WHEN_TYPING_DEFAULT, // the default is currently disabled - HIDE_WHEN_TYPING_ENABLE, - HIDE_WHEN_TYPING_DISABLE, -}; - enum seat_config_allow_constrain { - CONSTRAIN_DEFAULT, // the default is currently enabled + CONSTRAIN_DEFAULT, // the default is currently enabled CONSTRAIN_ENABLE, - CONSTRAIN_DISABLE, -}; - -enum seat_config_shortcuts_inhibit { - SHORTCUTS_INHIBIT_DEFAULT, // the default is currently enabled - SHORTCUTS_INHIBIT_ENABLE, - SHORTCUTS_INHIBIT_DISABLE, -}; - -enum seat_keyboard_grouping { - KEYBOARD_GROUP_DEFAULT, // the default is currently smart - KEYBOARD_GROUP_NONE, - KEYBOARD_GROUP_SMART, // keymap and repeat info -}; - -enum sway_input_idle_source { - IDLE_SOURCE_KEYBOARD = 1 << 0, - IDLE_SOURCE_POINTER = 1 << 1, - IDLE_SOURCE_TOUCH = 1 << 2, - IDLE_SOURCE_TABLET_PAD = 1 << 3, - IDLE_SOURCE_TABLET_TOOL = 1 << 4, - IDLE_SOURCE_SWITCH = 1 << 5, + CONSTRAIN_DISABLE }; /** @@ -242,35 +149,13 @@ struct seat_config { int fallback; // -1 means not set list_t *attachments; // list of seat_attachment configs int hide_cursor_timeout; - enum seat_config_hide_cursor_when_typing hide_cursor_when_typing; enum seat_config_allow_constrain allow_constrain; - enum seat_config_shortcuts_inhibit shortcuts_inhibit; - enum seat_keyboard_grouping keyboard_grouping; - uint32_t idle_inhibit_sources, idle_wake_sources; - struct { - char *name; - int size; - } xcursor_theme; }; -enum scale_filter_mode { - SCALE_FILTER_DEFAULT, // the default is currently smart - SCALE_FILTER_LINEAR, - SCALE_FILTER_NEAREST, - SCALE_FILTER_SMART, -}; - -enum render_bit_depth { - RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 for SDR, 10 for HDR - RENDER_BIT_DEPTH_6, - RENDER_BIT_DEPTH_8, - RENDER_BIT_DEPTH_10, -}; - -enum color_profile { - COLOR_PROFILE_DEFAULT, // default is Transform with NULL color_transform - COLOR_PROFILE_TRANSFORM, // use color_transform from output_config - COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES, // create transform from wlr_output +enum config_dpms { + DPMS_IGNORE, + DPMS_ON, + DPMS_OFF }; /** @@ -281,27 +166,16 @@ enum color_profile { struct output_config { char *name; int enabled; - int power; int width, height; float refresh_rate; - int custom_mode; - drmModeModeInfo drm_mode; int x, y; float scale; - enum scale_filter_mode scale_filter; int32_t transform; - enum wl_output_subpixel subpixel; - int max_render_time; // In milliseconds - int adaptive_sync; - enum render_bit_depth render_bit_depth; - enum color_profile color_profile; - struct wlr_color_transform *color_transform; - int allow_tearing; - int hdr; char *background; char *background_option; char *background_fallback; + enum config_dpms dpms_state; }; /** @@ -314,12 +188,6 @@ struct side_gaps { int left; }; -enum smart_gaps_mode { - SMART_GAPS_OFF, - SMART_GAPS_ON, - SMART_GAPS_INVERSE_OUTER, -}; - /** * Stores configuration for a workspace, regardless of whether the workspace * exists. @@ -331,17 +199,7 @@ struct workspace_config { struct side_gaps gaps_outer; }; -enum pango_markup_config { - PANGO_MARKUP_DISABLED = false, - PANGO_MARKUP_ENABLED = true, - PANGO_MARKUP_DEFAULT // The default is font dependent ("pango:" prefix) -}; - struct bar_config { - char *swaybar_command; - struct wl_client *client; - struct wl_listener client_destroy; - /** * One of "dock", "hide", "invisible" * @@ -368,7 +226,8 @@ struct bar_config { char *position; list_t *bindings; char *status_command; - enum pango_markup_config pango_markup; + bool pango_markup; + char *swaybar_command; char *font; int height; // -1 not defined bool workspace_buttons; @@ -379,9 +238,9 @@ struct bar_config { bool binding_mode_indicator; bool verbose; struct side_gaps gaps; + pid_t pid; int status_padding; int status_edge_padding; - uint32_t workspace_min_width; struct { char *background; char *statusline; @@ -437,16 +296,12 @@ struct border_colors { }; enum edge_border_types { - E_NONE, /**< Don't hide edge borders */ - E_VERTICAL, /**< hide vertical edge borders */ - E_HORIZONTAL, /**< hide horizontal edge borders */ - E_BOTH, /**< hide vertical and horizontal edge borders */ -}; - -enum edge_border_smart_types { - ESMART_OFF, - ESMART_ON, /**< hide edges if precisely one window is present in workspace */ - ESMART_NO_GAPS, /**< hide edges if one window and gaps to edge is zero */ + E_NONE, /**< Don't hide edge borders */ + E_VERTICAL, /**< hide vertical edge borders */ + E_HORIZONTAL, /**< hide horizontal edge borders */ + E_BOTH, /**< hide vertical and horizontal edge borders */ + E_SMART, /**< hide both if precisely one window is present in workspace */ + E_SMART_NO_GAPS, /**< hide both if one window and gaps to edge is zero */ }; enum sway_popup_during_fullscreen { @@ -455,35 +310,85 @@ enum sway_popup_during_fullscreen { POPUP_LEAVE, }; +enum command_context { + CONTEXT_CONFIG = 1, + CONTEXT_BINDING = 2, + CONTEXT_IPC = 4, + CONTEXT_CRITERIA = 8, + CONTEXT_ALL = 0xFFFFFFFF, +}; + +struct command_policy { + char *command; + uint32_t context; +}; + +enum secure_feature { + FEATURE_LOCK = 1, + FEATURE_PANEL = 2, + FEATURE_BACKGROUND = 4, + FEATURE_SCREENSHOT = 8, + FEATURE_FULLSCREEN = 16, + FEATURE_KEYBOARD = 32, + FEATURE_MOUSE = 64, +}; + +struct feature_policy { + char *program; + uint32_t features; +}; + +enum ipc_feature { + IPC_FEATURE_COMMAND = 1, + IPC_FEATURE_GET_WORKSPACES = 2, + IPC_FEATURE_GET_OUTPUTS = 4, + IPC_FEATURE_GET_TREE = 8, + IPC_FEATURE_GET_MARKS = 16, + IPC_FEATURE_GET_BAR_CONFIG = 32, + IPC_FEATURE_GET_VERSION = 64, + IPC_FEATURE_GET_INPUTS = 128, + IPC_FEATURE_EVENT_WORKSPACE = 256, + IPC_FEATURE_EVENT_OUTPUT = 512, + IPC_FEATURE_EVENT_MODE = 1024, + IPC_FEATURE_EVENT_WINDOW = 2048, + IPC_FEATURE_EVENT_BINDING = 4096, + IPC_FEATURE_EVENT_INPUT = 8192, + IPC_FEATURE_GET_SEATS = 16384, + + IPC_FEATURE_ALL_COMMANDS = + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384, + IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192, + + IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS, +}; + +struct ipc_policy { + char *program; + uint32_t features; +}; + enum focus_follows_mouse_mode { FOLLOWS_NO, FOLLOWS_YES, - FOLLOWS_ALWAYS, + FOLLOWS_ALWAYS }; enum focus_wrapping_mode { WRAP_NO, WRAP_YES, - WRAP_FORCE, - WRAP_WORKSPACE, + WRAP_FORCE }; enum mouse_warping_mode { WARP_NO, WARP_OUTPUT, - WARP_CONTAINER, + WARP_CONTAINER }; enum alignment { ALIGN_LEFT, ALIGN_CENTER, - ALIGN_RIGHT, -}; - -enum xwayland_mode { - XWAYLAND_MODE_DISABLED, - XWAYLAND_MODE_LAZY, - XWAYLAND_MODE_IMMEDIATE, + ALIGN_RIGHT }; /** @@ -499,13 +404,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; @@ -516,10 +421,9 @@ struct sway_config { char *floating_scroll_right_cmd; enum sway_container_layout default_orientation; enum sway_container_layout default_layout; - char *font; // Used for IPC. - PangoFontDescription *font_description; // Used internally for rendering and validating. - int font_height; - int font_baseline; + char *font; + size_t font_height; + size_t font_baseline; bool pango_markup; int titlebar_border_thickness; int titlebar_h_padding; @@ -527,12 +431,7 @@ struct sway_config { size_t urgent_timeout; enum sway_fowa focus_on_window_activation; enum sway_popup_during_fullscreen popup_during_fullscreen; - enum xwayland_mode xwayland; - - // swaybg - char *swaybg_command; - struct wl_client *swaybg_client; - struct wl_listener swaybg_client_destroy; + bool xwayland; // Flags enum focus_follows_mouse_mode focus_follows_mouse; @@ -546,17 +445,15 @@ struct sway_config { bool auto_back_and_forth; bool show_marks; enum alignment title_align; - bool primary_selection; bool tiling_drag; int tiling_drag_threshold; - enum smart_gaps_mode smart_gaps; + bool smart_gaps; int gaps_inner; struct side_gaps gaps_outer; list_t *config_chain; - bool user_config_path; const char *current_config_path; const char *current_config; int current_config_line_number; @@ -567,30 +464,28 @@ struct sway_config { int border_thickness; int floating_border_thickness; enum edge_border_types hide_edge_borders; - enum edge_border_smart_types hide_edge_borders_smart; - bool hide_lone_tab; + enum edge_border_types saved_edge_borders; // border colors struct { struct border_colors focused; struct border_colors focused_inactive; - struct border_colors focused_tab_title; struct border_colors unfocused; struct border_colors urgent; struct border_colors placeholder; float background[4]; } border_colors; - bool has_focused_tab_title; - // floating view int32_t floating_maximum_width; int32_t floating_maximum_height; int32_t floating_minimum_width; int32_t floating_minimum_height; - // The keysym to keycode translation - struct xkb_state *keysym_translation_state; + // Security + list_t *command_policies; + list_t *feature_policies; + list_t *ipc_policies; // Context for command handlers struct { @@ -601,7 +496,7 @@ struct sway_config { struct sway_node *node; struct sway_container *container; struct sway_workspace *workspace; - bool node_overridden; // True if the node is selected by means other than focus + bool using_criteria; struct { int argc; char **argv; @@ -618,7 +513,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. */ -void load_include_configs(const char *path, struct sway_config *config, +bool load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag); /** @@ -632,15 +527,10 @@ bool read_config(FILE *file, struct sway_config *config, */ void run_deferred_commands(void); -/** - * Run the binding commands that were deferred when initializing the inputs - */ -void run_deferred_bindings(void); - /** * Adds a warning entry to the swaynag instance used for errors. */ -void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); +void config_add_swaynag_warning(char *fmt, ...); /** * Free config struct @@ -660,10 +550,7 @@ struct input_config *new_input_config(const char* identifier); void merge_input_config(struct input_config *dst, struct input_config *src); -struct input_config *store_input_config(struct input_config *ic, char **error); - -void input_config_fill_rule_names(struct input_config *ic, - struct xkb_rule_names *rules); +struct input_config *store_input_config(struct input_config *ic); void free_input_config(struct input_config *ic); @@ -689,48 +576,38 @@ int output_name_cmp(const void *item, const void *data); void output_get_identifier(char *identifier, size_t len, struct sway_output *output); -const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter); - struct output_config *new_output_config(const char *name); -bool apply_output_configs(struct output_config **ocs, size_t ocs_len, - bool test_only, bool degrade_to_off); +void merge_output_config(struct output_config *dst, struct output_config *src); -void apply_stored_output_configs(void); +bool apply_output_config(struct output_config *oc, struct sway_output *output); -/** - * store_output_config stores a new output config. An output may be matched by - * three different config types, in order of precedence: Identifier, name and - * wildcard. When storing a config type of lower precedence, assume that the - * user wants the config to take immediate effect by superseding (clearing) the - * same values from higher presedence configuration. - */ -void store_output_config(struct output_config *oc); +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); -void request_modeset(void); -void force_modeset(void); -bool modeset_is_pending(void); - -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 free_gesture_binding(struct sway_gesture_binding *binding); - void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); void load_swaybar(struct bar_config *bar); 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); @@ -740,23 +617,14 @@ void free_bar_binding(struct bar_binding *binding); void free_workspace_config(struct workspace_config *wsc); /** - * Updates the value of config->font_height based on the metrics for title's - * font as reported by pango. + * Updates the value of config->font_height based on the max title height + * reported by each container. If recalculate is true, the containers will + * recalculate their heights before reporting. * * If the height has changed, all containers will be rearranged to take on the * new size. */ -void config_update_font_height(void); - -/** - * 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); +void config_update_font_height(bool recalculate); /* Global config singleton. */ extern struct sway_config *config; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index fad278e02..3eb583d5a 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -1,16 +1,11 @@ #ifndef _SWAY_CRITERIA_H #define _SWAY_CRITERIA_H -#define PCRE2_CODE_UNIT_WIDTH 8 -#include +#include #include "config.h" #include "list.h" #include "tree/view.h" -#if WLR_HAS_XWAYLAND -#include "sway/xwayland.h" -#endif - enum criteria_type { CT_COMMAND = 1 << 0, CT_ASSIGN_OUTPUT = 1 << 1, @@ -19,50 +14,31 @@ enum criteria_type { CT_NO_FOCUS = 1 << 4, }; -enum pattern_type { - PATTERN_PCRE2, - PATTERN_FOCUSED, -}; - -struct pattern { - enum pattern_type match_type; - pcre2_code *regex; -}; - struct criteria { enum criteria_type type; char *raw; // entire criteria string (for logging) char *cmdlist; char *target; // workspace or output name for `assign` criteria - struct pattern *title; - struct pattern *shell; - struct pattern *app_id; - struct pattern *con_mark; + pcre *title; + pcre *shell; + pcre *app_id; + pcre *con_mark; uint32_t con_id; // internal ID -#if WLR_HAS_XWAYLAND - struct pattern *class; +#if HAVE_XWAYLAND + pcre *class; uint32_t id; // X11 window ID - struct pattern *instance; - struct pattern *window_role; + pcre *instance; + pcre *window_role; enum atom_name window_type; #endif - bool all; bool floating; bool tiling; char urgent; // 'l' for latest or 'o' for oldest - struct pattern *workspace; - pid_t pid; - struct pattern *sandbox_engine; - struct pattern *sandbox_app_id; - struct pattern *sandbox_instance_id; - struct pattern *tag; + char *workspace; }; bool criteria_is_empty(struct criteria *criteria); -bool criteria_is_equal(struct criteria *left, struct criteria *right); - -bool criteria_already_exists(struct criteria *criteria); void criteria_destroy(struct criteria *criteria); @@ -84,8 +60,8 @@ struct criteria *criteria_parse(char *raw, char **error); list_t *criteria_for_view(struct sway_view *view, enum criteria_type types); /** - * Compile a list of containers matching the given criteria. + * Compile a list of views matching the given criteria. */ -list_t *criteria_get_containers(struct criteria *criteria); +list_t *criteria_get_views(struct criteria *criteria); #endif diff --git a/include/sway/debug.h b/include/sway/debug.h new file mode 100644 index 000000000..0e9bb0565 --- /dev/null +++ b/include/sway/debug.h @@ -0,0 +1,22 @@ +#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.h b/include/sway/desktop.h new file mode 100644 index 000000000..c969a76be --- /dev/null +++ b/include/sway/desktop.h @@ -0,0 +1,13 @@ +#include + +struct sway_container; +struct sway_view; + +void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, + bool whole); + +void desktop_damage_whole_container(struct sway_container *con); + +void desktop_damage_box(struct wlr_box *box); + +void desktop_damage_view(struct sway_view *view); diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 447ac8708..e5ed8a3de 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -1,48 +1,28 @@ #ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #include - -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 -}; +#include +#include "sway/server.h" struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; - struct wl_listener manager_destroy; struct wl_list inhibitors; + + struct wlr_idle *idle; }; struct sway_idle_inhibitor_v1 { - struct wlr_idle_inhibitor_v1 *wlr_inhibitor; + 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; }; -bool sway_idle_inhibit_v1_is_active( - struct sway_idle_inhibitor_v1 *inhibitor); - -void sway_idle_inhibit_v1_check_active(void); - -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); - -struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( - struct sway_view *view); - -void sway_idle_inhibit_v1_user_inhibitor_destroy( - struct sway_idle_inhibitor_v1 *inhibitor); - -bool sway_idle_inhibit_manager_v1_init(void); +void idle_inhibit_v1_check_active( + struct sway_idle_inhibit_manager_v1 *manager); +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/desktop/launcher.h b/include/sway/desktop/launcher.h deleted file mode 100644 index 412068a90..000000000 --- a/include/sway/desktop/launcher.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _SWAY_LAUNCHER_H -#define _SWAY_LAUNCHER_H - -#include -#include -#include "sway/input/seat.h" - -struct launcher_ctx { - pid_t pid; - char *fallback_name; - struct wlr_xdg_activation_token_v1 *token; - struct wl_listener token_destroy; - struct sway_seat *seat; - struct wl_listener seat_destroy; - - bool activated; - bool had_focused_surface; - - struct sway_node *node; - struct wl_listener node_destroy; - - struct wl_list link; // sway_server::pending_launcher_ctxs -}; - -struct launcher_ctx *launcher_ctx_find_pid(pid_t pid); - -struct sway_workspace *launcher_ctx_get_workspace(struct launcher_ctx *ctx); - -void launcher_ctx_consume(struct launcher_ctx *ctx); - -void launcher_ctx_destroy(struct launcher_ctx *ctx); - -struct launcher_ctx *launcher_ctx_create_internal(void); - -struct launcher_ctx *launcher_ctx_create( - struct wlr_xdg_activation_token_v1 *token, struct sway_node *node); - -const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx); - -#endif diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index dd7edb7a5..66e8c9a25 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -1,8 +1,6 @@ #ifndef _SWAY_TRANSACTION_H #define _SWAY_TRANSACTION_H #include -#include -#include /** * Transactions enable us to perform atomic layout updates. @@ -30,35 +28,21 @@ struct sway_view; */ void transaction_commit_dirty(void); -/* - * Same as transaction_commit_dirty, but signalling that this is a - * client-initiated change has already taken effect. - */ -void transaction_commit_dirty_client(void); - /** * Notify the transaction system that a view is ready for the new layout. * * When all views in the transaction are ready, the layout will be applied. - * - * A success boolean is returned denoting that this part of the transaction is - * ready. */ -bool transaction_notify_view_ready_by_serial(struct sway_view *view, +void transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial); /** * Notify the transaction system that a view is ready for the new layout, but - * identifying the instruction by geometry rather than by serial. + * identifying the instruction by width and height rather than by serial. * * This is used by xwayland views, as they don't have serials. - * - * A success boolean is returned denoting that this part of the transaction is - * ready. */ -bool transaction_notify_view_ready_by_geometry(struct sway_view *view, - double x, double y, int width, int height); - -void arrange_popups(struct wlr_scene_tree *popups); +void transaction_notify_view_ready_by_size(struct sway_view *view, + int width, int height); #endif diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 527d03500..98eb4679b 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -3,10 +3,8 @@ #include #include #include -#include -#include +#include #include "sway/input/seat.h" -#include "config.h" #define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32 @@ -23,8 +21,6 @@ struct sway_cursor { struct sway_node *node; } previous; struct wlr_xcursor_manager *xcursor_manager; - struct wl_list tablets; - struct wl_list tablet_pads; const char *image; struct wl_client *image_client; @@ -33,16 +29,6 @@ struct sway_cursor { struct wlr_pointer_constraint_v1 *active_constraint; pixman_region32_t confine; // invalid if active_constraint == NULL - bool active_confine_requires_warp; - - struct wl_listener hold_begin; - struct wl_listener hold_end; - struct wl_listener pinch_begin; - struct wl_listener pinch_update; - struct wl_listener pinch_end; - struct wl_listener swipe_begin; - struct wl_listener swipe_update; - struct wl_listener swipe_end; struct wl_listener motion; struct wl_listener motion_absolute; @@ -52,33 +38,22 @@ struct sway_cursor { struct wl_listener touch_down; struct wl_listener touch_up; - struct wl_listener touch_cancel; struct wl_listener touch_motion; - struct wl_listener touch_frame; - bool simulating_pointer_from_touch; - bool pointer_touch_up; - int32_t pointer_touch_id; struct wl_listener tool_axis; struct wl_listener tool_tip; - struct wl_listener tool_proximity; struct wl_listener tool_button; - bool simulating_pointer_from_tool_tip; - bool simulating_pointer_from_tool_button; uint32_t tool_buttons; struct wl_listener request_set_cursor; - struct wl_listener image_surface_destroy; struct wl_listener constraint_commit; struct wl_event_source *hide_source; bool hidden; - // This field is just a cache of the field in seat_config in order to avoid - // costly seat_config lookups on every keypress. HIDE_WHEN_TYPING_DEFAULT - // indicates that there is no cached value. - enum seat_config_hide_cursor_when_typing hide_when_typing; + // Mouse binding state + uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; size_t pressed_button_count; }; @@ -98,26 +73,24 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat); */ void cursor_rebase(struct sway_cursor *cursor); void cursor_rebase_all(void); -void cursor_update_image(struct sway_cursor *cursor, struct sway_node *node); -void cursor_handle_activity_from_idle_source(struct sway_cursor *cursor, - enum sway_input_idle_source idle_source); -void cursor_handle_activity_from_device(struct sway_cursor *cursor, - struct wlr_input_device *device); +void cursor_handle_activity(struct sway_cursor *cursor); void cursor_unhide(struct sway_cursor *cursor); int cursor_get_timeout(struct sway_cursor *cursor); -void cursor_notify_key_press(struct sway_cursor *cursor); -void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct wlr_input_device *device, double dx, double dy, - double dx_unaccel, double dy_unaccel); +/** + * 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 wl_pointer_button_state state); + enum wlr_button_state state); void dispatch_cursor_axis(struct sway_cursor *cursor, - struct wlr_pointer_axis_event *event); + struct wlr_event_pointer_axis *event); void cursor_set_image(struct sway_cursor *cursor, const char *image, struct wl_client *client); @@ -127,7 +100,7 @@ void cursor_set_image_surface(struct sway_cursor *cursor, struct wl_client *client); void cursor_warp_to_container(struct sway_cursor *cursor, - struct sway_container *container, bool force); + struct sway_container *container); void cursor_warp_to_workspace(struct sway_cursor *cursor, struct sway_workspace *workspace); @@ -145,6 +118,4 @@ uint32_t get_mouse_button(const char *name, char **error); const char *get_mouse_button_name(uint32_t button); -void handle_request_set_cursor_shape(struct wl_listener *listener, void *data); - #endif diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 5113844d8..e166a2377 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -1,46 +1,34 @@ #ifndef _SWAY_INPUT_INPUT_MANAGER_H #define _SWAY_INPUT_INPUT_MANAGER_H #include -#include +#include #include -#include -#include +#include "sway/server.h" #include "sway/config.h" #include "list.h" -struct sway_server; - struct sway_input_device { char *identifier; struct wlr_input_device *wlr_device; struct wl_list link; struct wl_listener device_destroy; - bool is_virtual; }; struct sway_input_manager { struct wl_list devices; struct wl_list seats; - struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; + struct wlr_input_inhibit_manager *inhibit; struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; - struct wlr_virtual_pointer_manager_v1 *virtual_pointer; - struct wlr_pointer_gestures_v1 *pointer_gestures; - struct wlr_transient_seat_manager_v1 *transient_seat_manager; struct wl_listener new_input; struct wl_listener inhibit_activate; struct wl_listener inhibit_deactivate; - struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; struct wl_listener virtual_keyboard_new; - struct wl_listener virtual_pointer_new; - struct wl_listener transient_seat_create; }; struct sway_input_manager *input_manager_create(struct sway_server *server); -void input_manager_finish(struct sway_input_manager *input); - bool input_manager_has_focus(struct sway_node *node); void input_manager_set_focus(struct sway_node *node); @@ -49,11 +37,9 @@ void input_manager_configure_xcursor(void); void input_manager_apply_input_config(struct input_config *input_config); -void input_manager_configure_all_input_mappings(void); - void input_manager_reset_input(struct sway_input_device *input_device); -void input_manager_reset_all_inputs(void); +void input_manager_reset_all_inputs(); void input_manager_apply_seat_config(struct seat_config *seat_config); @@ -76,6 +62,4 @@ 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 571d9e6fa..0c8ada0f7 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -8,7 +8,7 @@ /** * Get modifier mask from modifier name. * - * Returns the modifier mask or 0 if the name isn't found. + * Returns the modifer mask or 0 if the name isn't found. */ uint32_t get_modifier_mask_by_name(const char *name); @@ -50,13 +50,8 @@ struct sway_shortcut_state { struct sway_keyboard { struct sway_seat_device *seat_device; - struct wlr_keyboard *wlr; struct xkb_keymap *keymap; - xkb_layout_index_t effective_layout; - - int32_t repeat_rate; - int32_t repeat_delay; struct wl_listener keyboard_key; struct wl_listener keyboard_modifiers; @@ -64,26 +59,12 @@ struct sway_keyboard { struct sway_shortcut_state state_keysyms_translated; struct sway_shortcut_state state_keysyms_raw; struct sway_shortcut_state state_keycodes; - struct sway_shortcut_state state_pressed_sent; struct sway_binding *held_binding; struct wl_event_source *key_repeat_source; struct sway_binding *repeat_binding; }; -struct sway_keyboard_group { - struct wlr_keyboard_group *wlr_group; - struct sway_seat_device *seat_device; - struct wl_listener keyboard_key; - struct wl_listener keyboard_modifiers; - struct wl_listener enter; - struct wl_listener leave; - struct wl_list link; // sway_seat::keyboard_groups -}; - -struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, - char **error); - struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, struct sway_seat_device *device); diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h deleted file mode 100644 index 1f84a8e31..000000000 --- a/include/sway/input/libinput.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _SWAY_INPUT_LIBINPUT_H -#define _SWAY_INPUT_LIBINPUT_H -#include "sway/input/input-manager.h" - -bool sway_input_configure_libinput_device(struct sway_input_device *device); - -void sway_input_configure_libinput_device_send_events( - struct sway_input_device *device); - -void sway_input_reset_libinput_device(struct sway_input_device *device); - -bool sway_libinput_device_is_builtin(struct sway_input_device *device); - -#endif diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 428f96796..1c9354df4 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -1,67 +1,26 @@ #ifndef _SWAY_INPUT_SEAT_H #define _SWAY_INPUT_SEAT_H -#include #include -#include #include -#include #include -#include "sway/config.h" #include "sway/input/input-manager.h" -#include "sway/input/tablet.h" -#include "sway/input/text_input.h" struct sway_seat; struct sway_seatop_impl { - void (*button)(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state); - void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); - void (*pointer_axis)(struct sway_seat *seat, - struct wlr_pointer_axis_event *event); - void (*hold_begin)(struct sway_seat *seat, - struct wlr_pointer_hold_begin_event *event); - void (*hold_end)(struct sway_seat *seat, - struct wlr_pointer_hold_end_event *event); - void (*pinch_begin)(struct sway_seat *seat, - struct wlr_pointer_pinch_begin_event *event); - void (*pinch_update)(struct sway_seat *seat, - struct wlr_pointer_pinch_update_event *event); - void (*pinch_end)(struct sway_seat *seat, - struct wlr_pointer_pinch_end_event *event); - void (*swipe_begin)(struct sway_seat *seat, - struct wlr_pointer_swipe_begin_event *event); - void (*swipe_update)(struct sway_seat *seat, - struct wlr_pointer_swipe_update_event *event); - void (*swipe_end)(struct sway_seat *seat, - struct wlr_pointer_swipe_end_event *event); - void (*rebase)(struct sway_seat *seat, uint32_t time_msec); - void (*touch_motion)(struct sway_seat *seat, - struct wlr_touch_motion_event *event, double lx, double ly); - void (*touch_up)(struct sway_seat *seat, - struct wlr_touch_up_event *event); - void (*touch_down)(struct sway_seat *seat, - struct wlr_touch_down_event *event, double lx, double ly); - void (*touch_cancel)(struct sway_seat *seat, - struct wlr_touch_cancel_event *event); - void (*tablet_tool_motion)(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec); - void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, - uint32_t time_msec, enum wlr_tablet_tool_tip_state state); - void (*end)(struct sway_seat *seat); + void (*motion)(struct sway_seat *seat, uint32_t time_msec); + void (*finish)(struct sway_seat *seat); + void (*abort)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); - bool allow_set_cursor; + void (*render)(struct sway_seat *seat, struct sway_output *output, + pixman_region32_t *damage); }; 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 sway_tablet *tablet; - struct sway_tablet_pad *tablet_pad; struct wl_list link; // sway_seat::devices }; @@ -74,9 +33,16 @@ struct sway_seat_node { struct wl_listener destroy; }; -struct sway_drag { +struct sway_drag_icon { struct sway_seat *seat; - struct wlr_drag *wlr_drag; + struct wlr_drag_icon *wlr_drag_icon; + struct wl_list link; // sway_root::drag_icons + + double x, y; // in layout-local coordinates + + struct wl_listener surface_commit; + struct wl_listener map; + struct wl_listener unmap; struct wl_listener destroy; }; @@ -84,23 +50,16 @@ struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; - // Seat scene tree structure - // - scene_tree - // - drag icons - // - drag icon 1 - // - drag icon 2 - // - seatop specific stuff - struct wlr_scene_tree *scene_tree; - struct wlr_scene_tree *drag_icons; - bool has_focus; struct wl_list focus_stack; // list of containers in focus order struct sway_workspace *workspace; char *prev_workspace_name; // for workspace back_and_forth + // If the focused layer is set, views cannot receive keyboard focus struct wlr_layer_surface_v1 *focused_layer; - // If the exclusive layer is set, views cannot receive keyboard focus - bool has_exclusive_layer; + + // If exclusive_client is set, no other clients will receive input events + struct wl_client *exclusive_client; // Last touch point int32_t touch_id; @@ -109,47 +68,28 @@ 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; - uint32_t idle_inhibit_sources, idle_wake_sources; - - list_t *deferred_bindings; // struct sway_binding - - struct sway_input_method_relay im_relay; - struct wl_listener focus_destroy; struct wl_listener new_node; - struct wl_listener request_start_drag; - struct wl_listener start_drag; + struct wl_listener new_drag_icon; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; - struct wl_listener destroy; struct wl_list devices; // sway_seat_device::link - struct wl_list keyboard_groups; // sway_keyboard_group::link - struct wl_list keyboard_shortcuts_inhibitors; - // sway_keyboard_shortcuts_inhibitor::link struct wl_list link; // input_manager::seats }; struct sway_pointer_constraint { - struct sway_cursor *cursor; struct wlr_pointer_constraint_v1 *constraint; - struct wl_listener set_region; struct wl_listener destroy; }; -struct sway_keyboard_shortcuts_inhibitor { - struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor; - - struct wl_listener destroy; - - struct wl_list link; // sway_seat::keyboard_shortcuts_inhibitors -}; - struct sway_seat *seat_create(const char *seat_name); void seat_destroy(struct sway_seat *seat); @@ -160,9 +100,6 @@ void seat_add_device(struct sway_seat *seat, void seat_configure_device(struct sway_seat *seat, struct sway_input_device *device); -void seat_configure_device_mapping(struct sway_seat *seat, - struct sway_input_device *input_device); - void seat_reset_device(struct sway_seat *seat, struct sway_input_device *input_device); @@ -193,17 +130,13 @@ void seat_set_focus_surface(struct sway_seat *seat, void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface_v1 *layer); -void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client); +void seat_set_exclusive_client(struct sway_seat *seat, + struct wl_client *client); 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); /** @@ -247,103 +180,49 @@ struct seat_config *seat_get_config(struct sway_seat *seat); struct seat_config *seat_get_config_by_name(const char *name); -void seat_idle_notify_activity(struct sway_seat *seat, - enum sway_input_idle_source source); - bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); -void drag_icons_update_position(struct sway_seat *seat); +void drag_icon_update_position(struct sway_drag_icon *icon); -enum wlr_edges find_resize_edge(struct sway_container *cont, - struct wlr_surface *surface, struct sway_cursor *cursor); - -void seatop_begin_default(struct sway_seat *seat); - -void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - double sx, double sy); - -void seatop_begin_down_on_surface(struct sway_seat *seat, - struct wlr_surface *surface, double sx, double sy); - -void seatop_begin_touch_down(struct sway_seat *seat, struct wlr_surface *surface, - struct wlr_touch_down_event *event, double sx, double sy, double lx, double ly); +void seatop_begin_down(struct sway_seat *seat, + struct sway_container *con, uint32_t button, int sx, int sy); void seatop_begin_move_floating(struct sway_seat *seat, - struct sway_container *con); + struct sway_container *con, uint32_t button); void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con); + struct sway_container *con, uint32_t button); void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con); + struct sway_container *con, uint32_t button); void seatop_begin_resize_floating(struct sway_seat *seat, - struct sway_container *con, enum wlr_edges edge); + struct sway_container *con, uint32_t button, enum wlr_edges edge); void seatop_begin_resize_tiling(struct sway_seat *seat, - struct sway_container *con, enum wlr_edges edge); + struct sway_container *con, uint32_t button, enum wlr_edges edge); struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, struct sway_workspace *workspace); void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, - uint32_t button, enum wl_pointer_button_state state); + uint32_t button, enum wlr_button_state state); void seat_consider_warp_to_focus(struct sway_seat *seat); -void seatop_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state); +bool seat_doing_seatop(struct sway_seat *seat); -void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); - -void seatop_pointer_axis(struct sway_seat *seat, - struct wlr_pointer_axis_event *event); - -void seatop_tablet_tool_tip(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec, - enum wlr_tablet_tool_tip_state state); - -void seatop_tablet_tool_motion(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec); - -void seatop_hold_begin(struct sway_seat *seat, - struct wlr_pointer_hold_begin_event *event); -void seatop_hold_end(struct sway_seat *seat, - struct wlr_pointer_hold_end_event *event); - -void seatop_pinch_begin(struct sway_seat *seat, - struct wlr_pointer_pinch_begin_event *event); -void seatop_pinch_update(struct sway_seat *seat, - struct wlr_pointer_pinch_update_event *event); -void seatop_pinch_end(struct sway_seat *seat, - struct wlr_pointer_pinch_end_event *event); - -void seatop_swipe_begin(struct sway_seat *seat, - struct wlr_pointer_swipe_begin_event *event); -void seatop_swipe_update(struct sway_seat *seat, - struct wlr_pointer_swipe_update_event *event); -void seatop_swipe_end(struct sway_seat *seat, - struct wlr_pointer_swipe_end_event *event); - -void seatop_touch_motion(struct sway_seat *seat, - struct wlr_touch_motion_event *event, double lx, double ly); - -void seatop_touch_up(struct sway_seat *seat, - struct wlr_touch_up_event *event); - -void seatop_touch_down(struct sway_seat *seat, - struct wlr_touch_down_event *event, double lx, double ly); - -void seatop_touch_cancel(struct sway_seat *seat, - struct wlr_touch_cancel_event *event); - -void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); +void seatop_motion(struct sway_seat *seat, uint32_t time_msec); /** - * End a seatop (ie. free any seatop specific resources). + * End a seatop and apply the affects. */ -void seatop_end(struct sway_seat *seat); +void seatop_finish(struct sway_seat *seat); + +/** + * End a seatop without applying the affects. + */ +void seatop_abort(struct sway_seat *seat); /** * Instructs the seatop implementation to drop any references to the given @@ -352,21 +231,12 @@ void seatop_end(struct sway_seat *seat); */ void seatop_unref(struct sway_seat *seat, struct sway_container *con); -bool seatop_allows_set_cursor(struct sway_seat *seat); - /** - * Returns the keyboard shortcuts inhibitor that applies to the given surface - * or NULL if none exists. + * Instructs a seatop to render anything that it needs to render + * (eg. dropzone for move-tiling) */ -struct sway_keyboard_shortcuts_inhibitor * -keyboard_shortcuts_inhibitor_get_for_surface(const struct sway_seat *seat, - const struct wlr_surface *surface); +void seatop_render(struct sway_seat *seat, struct sway_output *output, + pixman_region32_t *damage); -/** - * Returns the keyboard shortcuts inhibitor that applies to the currently - * focused surface of a seat or NULL if none exists. - */ -struct sway_keyboard_shortcuts_inhibitor * -keyboard_shortcuts_inhibitor_get_for_focused_surface(const struct sway_seat *seat); #endif diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h deleted file mode 100644 index de6787b7f..000000000 --- a/include/sway/input/switch.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 wlr_switch *wlr; - enum wlr_switch_state state; - enum wlr_switch_type type; - - 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); - -void sway_switch_retrigger_bindings_for_all(void); - -#endif diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h deleted file mode 100644 index 2fa5db6d8..000000000 --- a/include/sway/input/tablet.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef _SWAY_INPUT_TABLET_H -#define _SWAY_INPUT_TABLET_H -#include - -struct sway_seat; -struct wlr_tablet_tool; - -struct sway_tablet { - struct wl_list link; - struct sway_seat_device *seat_device; - struct wlr_tablet_v2_tablet *tablet_v2; -}; - -enum sway_tablet_tool_mode { - SWAY_TABLET_TOOL_MODE_ABSOLUTE, - SWAY_TABLET_TOOL_MODE_RELATIVE, -}; - -struct sway_tablet_tool { - struct sway_seat *seat; - struct sway_tablet *tablet; - struct wlr_tablet_v2_tablet_tool *tablet_v2_tool; - - enum sway_tablet_tool_mode mode; - double tilt_x, tilt_y; - - struct wl_listener set_cursor; - struct wl_listener tool_destroy; -}; - -struct sway_tablet_pad { - struct wl_list link; - struct sway_seat_device *seat_device; - struct sway_tablet *tablet; - struct wlr_tablet_pad *wlr; - struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; - - struct wl_listener attach; - struct wl_listener button; - struct wl_listener ring; - struct wl_listener strip; - - struct wlr_surface *current_surface; - struct wl_listener surface_destroy; - - struct wl_listener tablet_destroy; -}; - -struct sway_tablet *sway_tablet_create(struct sway_seat *seat, - struct sway_seat_device *device); - -void sway_configure_tablet(struct sway_tablet *tablet); - -void sway_tablet_destroy(struct sway_tablet *tablet); - -void sway_tablet_tool_configure(struct sway_tablet *tablet, - struct wlr_tablet_tool *wlr_tool); - -struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, - struct sway_seat_device *device); - -void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad); - -void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); - -void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, - struct wlr_surface *surface); - -#endif diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h deleted file mode 100644 index 1818749ad..000000000 --- a/include/sway/input/text_input.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef _SWAY_INPUT_TEXT_INPUT_H -#define _SWAY_INPUT_TEXT_INPUT_H - -#include -#include -#include - -/** - * The relay structure manages the relationship between text-input and - * input_method interfaces on a given seat. Multiple text-input interfaces may - * be bound to a relay, but at most one will be focused (receiving events) at - * a time. At most one input-method interface may be bound to the seat. The - * relay manages life cycle of both sides. When both sides are present and - * focused, the relay passes messages between them. - * - * Text input focus is a subset of keyboard focus - if the text-input is - * in the focused state, wl_keyboard sent an enter as well. However, having - * wl_keyboard focused doesn't mean that text-input will be focused. - */ -struct sway_input_method_relay { - struct sway_seat *seat; - - struct wl_list text_inputs; // sway_text_input::link - struct wl_list input_popups; // sway_input_popup::link - struct wlr_input_method_v2 *input_method; // doesn't have to be present - - struct wl_listener text_input_new; - struct wl_listener text_input_manager_destroy; - - struct wl_listener input_method_new; - struct wl_listener input_method_manager_destroy; - struct wl_listener input_method_commit; - struct wl_listener input_method_new_popup_surface; - struct wl_listener input_method_grab_keyboard; - struct wl_listener input_method_destroy; - - struct wl_listener input_method_keyboard_grab_destroy; -}; - - -struct sway_text_input { - struct sway_input_method_relay *relay; - - struct wlr_text_input_v3 *input; - // The surface getting seat's focus. Stored for when text-input cannot - // be sent an enter event immediately after getting focus, e.g. when - // there's no input method available. Cleared once text-input is entered. - struct wlr_surface *pending_focused_surface; - - struct wl_list link; - - struct wl_listener pending_focused_surface_destroy; - - struct wl_listener text_input_enable; - struct wl_listener text_input_commit; - struct wl_listener text_input_disable; - struct wl_listener text_input_destroy; -}; - -void sway_input_method_relay_init(struct sway_seat *seat, - struct sway_input_method_relay *relay); - -void sway_input_method_relay_finish(struct sway_input_method_relay *relay); - -// Updates currently focused surface. Surface must belong to the same seat. -void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay, - struct wlr_surface *surface); - -struct sway_text_input *sway_text_input_create( - struct sway_input_method_relay *relay, - struct wlr_text_input_v3 *text_input); - -#endif diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h deleted file mode 100644 index 7e838ed2a..000000000 --- a/include/sway/input/text_input_popup.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _SWAY_INPUT_TEXT_INPUT_POPUP_H -#define _SWAY_INPUT_TEXT_INPUT_POPUP_H - -#include "sway/tree/view.h" - -struct sway_input_popup { - struct sway_input_method_relay *relay; - - struct wlr_scene_tree *scene_tree; - struct sway_popup_desc desc; - struct wlr_input_popup_surface_v2 *popup_surface; - struct wlr_output *fixed_output; - - struct wl_list link; - - struct wl_listener popup_destroy; - struct wl_listener popup_surface_commit; - struct wl_listener popup_surface_map; - struct wl_listener popup_surface_unmap; - - struct wl_listener focused_surface_unmap; -}; -#endif diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index bc9f49856..3e584dbbb 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h @@ -1,16 +1,12 @@ #ifndef _SWAY_IPC_JSON_H #define _SWAY_IPC_JSON_H #include -#include "sway/output.h" #include "sway/tree/container.h" #include "sway/input/input-manager.h" json_object *ipc_json_get_version(void); -json_object *ipc_json_get_binding_mode(void); - json_object *ipc_json_describe_disabled_output(struct sway_output *o); -json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *o); json_object *ipc_json_describe_node(struct sway_node *node); json_object *ipc_json_describe_node_recursive(struct sway_node *node); json_object *ipc_json_describe_input(struct sway_input_device *device); diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index d4c009427..3c43f74d2 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -2,7 +2,6 @@ #define _SWAY_IPC_SERVER_H #include #include "sway/config.h" -#include "sway/input/input-manager.h" #include "sway/tree/container.h" #include "ipc.h" @@ -20,7 +19,5 @@ void ipc_event_bar_state_update(struct bar_config *bar); void ipc_event_mode(const char *mode, bool pango); void ipc_event_shutdown(const char *reason); void ipc_event_binding(struct sway_binding *binding); -void ipc_event_input(const char *change, struct sway_input_device *device); -void ipc_event_output(void); #endif diff --git a/include/sway/layers.h b/include/sway/layers.h index e257da0bd..51878fc9c 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -1,48 +1,28 @@ #ifndef _SWAY_LAYERS_H #define _SWAY_LAYERS_H #include -#include +#include +#include #include -#include "sway/tree/view.h" struct sway_layer_surface { + struct wlr_layer_surface_v1 *layer_surface; + struct wl_list link; + + struct wl_listener destroy; struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; - struct wl_listener node_destroy; - struct wl_listener new_popup; + struct wl_listener output_destroy; - bool mapped; - - struct wlr_scene_tree *popups; - struct sway_popup_desc desc; - - struct sway_output *output; - struct wl_list link; // sway_output.layer_surfaces - - struct wlr_scene_layer_surface_v1 *scene; - struct wlr_scene_tree *tree; - struct wlr_layer_surface_v1 *layer_surface; -}; - -struct sway_layer_popup { - struct wlr_xdg_popup *wlr_popup; - struct wlr_scene_tree *scene; - struct sway_layer_surface *toplevel; - - struct wl_listener destroy; - struct wl_listener new_popup; - struct wl_listener commit; - struct wl_listener reposition; + bool configured; + struct wlr_box geo; }; struct sway_output; - -struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( - struct wlr_surface *surface); - void arrange_layers(struct sway_output *output); -void destroy_layers(struct sway_output *output); +struct sway_layer_surface *layer_from_wlr_layer_surface_v1( + struct wlr_layer_surface_v1 *layer_surface); #endif diff --git a/include/sway/lock.h b/include/sway/lock.h deleted file mode 100644 index 5be0f9695..000000000 --- a/include/sway/lock.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _SWAY_LOCK_H -#define _SWAY_LOCK_H - -void arrange_locks(void); - -#endif diff --git a/include/sway/output.h b/include/sway/output.h index ae2e50d36..ea7a21741 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -2,10 +2,9 @@ #define _SWAY_OUTPUT_H #include #include -#include -#include +#include +#include #include -#include #include "config.h" #include "sway/tree/node.h" #include "sway/tree/view.h" @@ -20,64 +19,38 @@ struct sway_output_state { struct sway_output { struct sway_node node; - - struct { - struct wlr_scene_tree *shell_background; - struct wlr_scene_tree *shell_bottom; - struct wlr_scene_tree *tiling; - struct wlr_scene_tree *fullscreen; - struct wlr_scene_tree *shell_top; - struct wlr_scene_tree *shell_overlay; - struct wlr_scene_tree *session_lock; - } layers; - - // when a container is fullscreen, in case the fullscreen surface is - // translucent (can see behind) we must make sure that the background is a - // solid color in order to conform to the wayland protocol. This rect - // ensures that when looking through a surface, all that will be seen - // is black. - struct wlr_scene_rect *fullscreen_background; - struct wlr_output *wlr_output; - struct wlr_scene_output *scene_output; struct sway_server *server; struct wl_list link; + struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; - int lx, ly; // layout coords - int width, height; // transformed buffer size - enum wl_output_subpixel detected_subpixel; - enum scale_filter_mode scale_filter; + struct timespec last_frame; + struct wlr_output_damage *damage; - bool enabled; + int lx, ly; + int width, height; + + bool enabled, configured; list_t *workspaces; - struct wl_list layer_surfaces; // sway_layer_surface.link struct sway_output_state current; - struct wl_listener layout_destroy; + struct wl_client *swaybg_client; + struct wl_listener destroy; + struct wl_listener mode; + struct wl_listener transform; + struct wl_listener scale; struct wl_listener present; - struct wl_listener frame; - struct wl_listener request_state; + struct wl_listener damage_destroy; + struct wl_listener damage_frame; + struct wl_listener swaybg_client_destroy; - struct wlr_color_transform *color_transform; - struct wlr_ext_workspace_group_handle_v1 *ext_workspace_group; - - struct timespec last_presentation; - uint32_t refresh_nsec; - int max_render_time; // In milliseconds - struct wl_event_source *repaint_timer; - - bool allow_tearing; - bool hdr; -}; - -struct sway_output_non_desktop { - struct wlr_output *wlr_output; - - struct wl_listener destroy; + struct { + struct wl_signal destroy; + } events; }; struct sway_output *output_create(struct wlr_output *wlr_output); @@ -91,33 +64,69 @@ struct sway_output *output_from_wlr_output(struct wlr_output *output); struct sway_output *output_get_in_direction(struct sway_output *reference, enum wlr_direction direction); -void output_configure_scene(struct sway_output *output, - struct wlr_scene_node *node, float opacity); - void output_add_workspace(struct sway_output *output, struct sway_workspace *workspace); typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, + struct wlr_surface *surface, struct wlr_box *box, float rotation, void *user_data); -bool output_match_name_or_id(struct sway_output *output, - const char *name_or_id); +void output_damage_whole(struct sway_output *output); + +void output_damage_surface(struct sway_output *output, double ox, double oy, + struct wlr_surface *surface, bool whole); + +void output_damage_from_view(struct sway_output *output, + struct sway_view *view); + +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); -// this includes all the outputs, including disabled ones -struct sway_output *all_output_by_name_or_id(const char *name_or_id); - void output_sort_workspaces(struct sway_output *output); -void output_enable(struct sway_output *output); +struct output_config *output_find_config(struct sway_output *output); + +void output_enable(struct sway_output *output, struct output_config *oc); void output_disable(struct sway_output *output); +bool output_has_opaque_overlay_layer_surface(struct sway_output *output); + struct sway_workspace *output_get_active_workspace(struct sway_output *output); +void output_render(struct sway_output *output, struct timespec *when, + pixman_region32_t *damage); + +void output_surface_for_each_surface(struct sway_output *output, + struct wlr_surface *surface, double ox, double oy, + sway_surface_iterator_func_t iterator, void *user_data); + +void output_view_for_each_surface(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data); + +void output_view_for_each_popup(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data); + +void output_layer_for_each_surface(struct sway_output *output, + struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, + void *user_data); + +#if HAVE_XWAYLAND +void output_unmanaged_for_each_surface(struct sway_output *output, + struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, + void *user_data); +#endif + +void output_drag_icons_for_each_surface(struct sway_output *output, + struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, + void *user_data); + void output_for_each_workspace(struct sway_output *output, void (*f)(struct sway_workspace *ws, void *data), void *data); @@ -132,22 +141,17 @@ struct sway_container *output_find_container(struct sway_output *output, void output_get_box(struct sway_output *output, struct wlr_box *box); -bool output_supports_hdr(struct wlr_output *output, const char **unsupported_reason_ptr); - enum sway_container_layout output_get_default_layout( struct sway_output *output); +void render_rect(struct wlr_output *wlr_output, + pixman_region32_t *output_damage, const struct wlr_box *_box, + float color[static 4]); + +void premultiply_alpha(float color[4], float opacity); + +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); - -void handle_output_power_manager_set_mode(struct wl_listener *listener, - void *data); - -struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); - -void update_output_manager_config(struct sway_server *server); - #endif diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h deleted file mode 100644 index 2649d7c29..000000000 --- a/include/sway/scene_descriptor.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Across a wayland compositor, there are multiple shells: It can be - * a toplevel, or a layer_shell, or even something more meta like a drag - * icon or highlight indicators when dragging windows around. - * - * This object lets us store values that represent these modes of operation - * and keep track of what object is being represented. - */ -#ifndef _SWAY_SCENE_DESCRIPTOR_H -#define _SWAY_SCENE_DESCRIPTOR_H -#include - -enum sway_scene_descriptor_type { - SWAY_SCENE_DESC_BUFFER_TIMER, - SWAY_SCENE_DESC_NON_INTERACTIVE, - SWAY_SCENE_DESC_CONTAINER, - SWAY_SCENE_DESC_VIEW, - SWAY_SCENE_DESC_LAYER_SHELL, - SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, - SWAY_SCENE_DESC_POPUP, - SWAY_SCENE_DESC_DRAG_ICON, -}; - -bool scene_descriptor_assign(struct wlr_scene_node *node, - enum sway_scene_descriptor_type type, void *data); - -void *scene_descriptor_try_get(struct wlr_scene_node *node, - enum sway_scene_descriptor_type type); - -void scene_descriptor_destroy(struct wlr_scene_node *node, - enum sway_scene_descriptor_type type); - -#endif diff --git a/include/sway/security.h b/include/sway/security.h new file mode 100644 index 000000000..0edffdfa7 --- /dev/null +++ b/include/sway/security.h @@ -0,0 +1,18 @@ +#ifndef _SWAY_SECURITY_H +#define _SWAY_SECURITY_H +#include +#include "sway/config.h" + +uint32_t get_feature_policy_mask(pid_t pid); +uint32_t get_ipc_policy_mask(pid_t pid); +uint32_t get_command_policy_mask(const char *cmd); + +struct feature_policy *get_feature_policy(const char *name); + +const char *command_policy_str(enum command_context context); + +struct feature_policy *alloc_feature_policy(const char *program); +struct ipc_policy *alloc_ipc_policy(const char *program); +struct command_policy *alloc_command_policy(const char *command); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index 8c8114882..5eef7c1a9 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -1,65 +1,52 @@ #ifndef _SWAY_SERVER_H #define _SWAY_SERVER_H #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "config.h" #include "list.h" -#include "sway/desktop/idle_inhibit_v1.h" -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND #include "sway/xwayland.h" #endif -struct sway_transaction; - -struct sway_session_lock { - struct wlr_session_lock_v1 *lock; - struct wlr_surface *focused; - bool abandoned; - - struct wl_list outputs; // struct sway_session_lock_output - - // invalid if the session is abandoned - struct wl_listener new_surface; - struct wl_listener unlock; - struct wl_listener destroy; -}; - struct sway_server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; - char *socket; + const char *socket; struct wlr_backend *backend; - struct wlr_session *session; - // secondary headless backend used for creating virtual outputs on-the-fly - struct wlr_backend *headless_backend; - struct wlr_renderer *renderer; - struct wlr_allocator *allocator; + struct wlr_backend *noop_backend; struct wlr_compositor *compositor; - - struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; - struct wlr_data_device_manager *data_device_manager; struct sway_input_manager *input; struct wl_listener new_output; - struct wl_listener renderer_lost; - struct wl_event_source *recreating_renderer; - struct wlr_idle_notifier_v1 *idle_notifier_v1; - struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; + struct wlr_idle *idle; + struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; struct wlr_layer_shell_v1 *layer_shell; struct wl_listener layer_shell_surface; + struct wlr_xdg_shell_v6 *xdg_shell_v6; + struct wl_listener xdg_shell_v6_surface; + struct wlr_xdg_shell *xdg_shell; - struct wl_listener xdg_shell_toplevel; + struct wl_listener xdg_shell_surface; - struct wlr_tablet_manager_v2 *tablet_v2; - -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND struct sway_xwayland xwayland; struct wl_listener xwayland_surface; struct wl_listener xwayland_ready; @@ -75,98 +62,20 @@ struct sway_server { struct wl_listener xdg_decoration; struct wl_list xdg_decorations; // sway_xdg_decoration::link - struct wlr_drm_lease_v1_manager *drm_lease_manager; - struct wl_listener drm_lease_request; + struct wlr_presentation *presentation; struct wlr_pointer_constraints_v1 *pointer_constraints; struct wl_listener pointer_constraint; - struct wlr_xdg_output_manager_v1 *xdg_output_manager_v1; - - struct wlr_output_manager_v1 *output_manager_v1; - struct wl_listener output_manager_apply; - struct wl_listener output_manager_test; - - struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; - struct wl_listener gamma_control_set_gamma; - - struct { - struct sway_session_lock *lock; - struct wlr_session_lock_manager_v1 *manager; - - struct wl_listener new_lock; - struct wl_listener manager_destroy; - } session_lock; - - struct wlr_output_power_manager_v1 *output_power_manager_v1; - struct wl_listener output_power_manager_set_mode; - struct wlr_input_method_manager_v2 *input_method; - struct wlr_text_input_manager_v3 *text_input; - struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list; - struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; - struct wlr_content_type_manager_v1 *content_type_manager_v1; - struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1; - struct wlr_ext_data_control_manager_v1 *ext_data_control_manager_v1; - struct wlr_screencopy_manager_v1 *screencopy_manager_v1; - struct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_manager_v1; - struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; - struct wlr_security_context_manager_v1 *security_context_manager_v1; - - struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_foreign_toplevel_image_capture_source_manager_v1; - struct wl_listener new_foreign_toplevel_capture_request; - - struct wlr_xdg_activation_v1 *xdg_activation_v1; - struct wl_listener xdg_activation_v1_request_activate; - struct wl_listener xdg_activation_v1_new_token; - - struct wl_listener xdg_toplevel_tag_manager_v1_set_tag; - - struct wl_listener request_set_cursor_shape; - - struct wlr_tearing_control_manager_v1 *tearing_control_v1; - struct wl_listener tearing_control_new_object; - struct wl_list tearing_controllers; // sway_tearing_controller::link - - struct wlr_ext_workspace_manager_v1 *workspace_manager_v1; - struct wl_listener workspace_manager_v1_commit; - - struct wl_list pending_launcher_ctxs; // launcher_ctx::link - - // The timeout for transactions, after which a transaction is applied - // regardless of readiness. size_t txn_timeout_ms; - - // Stores a transaction after it has been committed, but is waiting for - // views to ack the new dimensions before being applied. A queued - // transaction is frozen and must not have new instructions added to it. - struct sway_transaction *queued_transaction; - - // Stores a pending transaction that will be committed once the existing - // queued transaction is applied and freed. The pending transaction can be - // updated with new instructions as needed. - struct sway_transaction *pending_transaction; - - // Stores the nodes that have been marked as "dirty" and will be put into - // the pending transaction. + list_t *transactions; list_t *dirty_nodes; - - struct wl_event_source *delayed_modeset; }; -extern 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 -}; - -extern struct sway_debug debug; - -extern bool unsupported_gpu_detected; - -void sway_terminate(int exit_code); +struct sway_server server; +/* Prepares an unprivileged server_init by performing all privileged operations in advance */ +bool server_privileged_prepare(struct sway_server *server); bool server_init(struct sway_server *server); void server_fini(struct sway_server *server); bool server_start(struct sway_server *server); @@ -176,25 +85,13 @@ void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); -bool sway_session_lock_init(void); -void sway_session_lock_add_output(struct sway_session_lock *lock, - struct sway_output *output); -bool sway_session_lock_has_surface(struct sway_session_lock *lock, - struct wlr_surface *surface); -void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); -#if WLR_HAS_XWAYLAND +void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); +void handle_xdg_shell_surface(struct wl_listener *listener, void *data); +#if HAVE_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); #endif void handle_server_decoration(struct wl_listener *listener, void *data); void handle_xdg_decoration(struct wl_listener *listener, void *data); void handle_pointer_constraint(struct wl_listener *listener, void *data); -void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, - void *data); -void xdg_activation_v1_handle_new_token(struct wl_listener *listener, - void *data); - -void set_rr_scheduling(void); - -void handle_new_tearing_hint(struct wl_listener *listener, void *data); #endif diff --git a/include/sway/sway_text_node.h b/include/sway/sway_text_node.h deleted file mode 100644 index 0d4209bb6..000000000 --- a/include/sway/sway_text_node.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _SWAY_BUFFER_H -#define _SWAY_BUFFER_H -#include - -struct sway_text_node { - int width; - int max_width; - int height; - int baseline; - bool pango_markup; - float color[4]; - float background[4]; - - struct wlr_scene_node *node; -}; - -struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, - char *text, float color[4], bool pango_markup); - -void sway_text_node_set_color(struct sway_text_node *node, float color[4]); - -void sway_text_node_set_text(struct sway_text_node *node, char *text); - -void sway_text_node_set_max_width(struct sway_text_node *node, int max_width); - -void sway_text_node_set_background(struct sway_text_node *node, float background[4]); - -#endif diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 03bd52c33..5a1787392 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h @@ -1,13 +1,9 @@ #ifndef _SWAY_SWAYNAG_H #define _SWAY_SWAYNAG_H -#include -#include "stringop.h" struct swaynag_instance { - struct wl_client *client; - struct wl_listener client_destroy; - const char *args; + pid_t pid; int fd[2]; bool detailed; }; @@ -19,10 +15,13 @@ 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, - const char *fmt, ...) _SWAY_ATTRIB_PRINTF(3, 4); + const char *fmt, ...); // If swaynag->detailed, close swaynag->fd[1] so swaynag displays void swaynag_show(struct swaynag_instance *swaynag); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index d8780544d..f7a4ac37e 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -2,8 +2,8 @@ #define _SWAY_CONTAINER_H #include #include -#include -#include +#include +#include #include "list.h" #include "sway/tree/node.h" @@ -46,9 +46,9 @@ struct sway_container_state { enum sway_fullscreen_mode fullscreen_mode; - struct sway_workspace *workspace; // NULL when hidden in the scratchpad - struct sway_container *parent; // NULL if container in root of workspace - list_t *children; // struct sway_container + struct sway_workspace *workspace; + struct sway_container *parent; + list_t *children; struct sway_container *focused_inactive_child; bool focused; @@ -60,7 +60,6 @@ struct sway_container_state { bool border_left; bool border_right; - // These are in layout coordinates. double content_x, content_y; double content_width, content_height; }; @@ -69,77 +68,84 @@ struct sway_container { struct sway_node node; struct sway_view *view; - struct wlr_scene_tree *scene_tree; - - struct { - struct wlr_scene_tree *tree; - - struct wlr_scene_tree *border; - struct wlr_scene_tree *background; - - struct sway_text_node *title_text; - struct sway_text_node *marks_text; - } title_bar; - - struct { - struct wlr_scene_tree *tree; - - struct wlr_scene_rect *top; - struct wlr_scene_rect *bottom; - struct wlr_scene_rect *left; - struct wlr_scene_rect *right; - } border; - - struct wlr_scene_tree *content_tree; - + // The pending state is the main container properties, and the current state is in the below struct. + // This means most places of the code can refer to the main variables (pending state) and it'll just work. struct sway_container_state current; - struct sway_container_state pending; char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar - int title_width; - - char *title_format; + enum sway_container_layout layout; enum sway_container_layout prev_split_layout; - // Whether stickiness has been enabled on this container. Use - // `container_is_sticky_[or_child]` rather than accessing this field - // directly; it'll also check that the container is floating. bool is_sticky; // For C_ROOT, this has no meaning // For other types, this is the position in layout coordinates // Includes borders + double x, y; + double width, height; double saved_x, saved_y; double saved_width, saved_height; + // These are in layout coordinates. + double content_x, content_y; + int content_width, content_height; + + // In most cases this is the same as the content x and y, but if the view + // refuses to resize to the content dimensions then it can be smaller. + // These are in layout coordinates. + double surface_x, surface_y; + double surface_width, surface_height; + + enum sway_fullscreen_mode fullscreen_mode; + + enum sway_container_border border; + // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD // border which we use to restore when the view returns to SSD. enum sway_container_border saved_border; - // The share of the space of parent container this container occupies - double width_fraction; - double height_fraction; + int border_thickness; + bool border_top; + bool border_bottom; + bool border_left; + bool border_right; - // The share of space of the parent container that all children occupy - // Used for doing the resize calculations - double child_total_width; - double child_total_height; + // The gaps currently applied to the container. + struct { + int top; + int right; + int bottom; + int left; + } current_gaps; + + struct sway_workspace *workspace; // NULL when hidden in the scratchpad + struct sway_container *parent; // NULL if container in root of workspace + list_t *children; // struct sway_container + + // Outputs currently being intersected + list_t *outputs; // struct sway_output // Indicates that the container is a scratchpad container. // Both hidden and visible scratchpad containers have scratchpad=true. // Hidden scratchpad containers have a NULL parent. bool scratchpad; - // Stores last output size and position for adjusting coordinates of - // scratchpad windows. - // Unused for non-scratchpad windows. - struct wlr_box transform; - float alpha; + struct wlr_texture *title_focused; + struct wlr_texture *title_focused_inactive; + struct wlr_texture *title_unfocused; + struct wlr_texture *title_urgent; + size_t title_height; + size_t title_baseline; + list_t *marks; // char * + struct wlr_texture *marks_focused; + struct wlr_texture *marks_focused_inactive; + struct wlr_texture *marks_unfocused; + struct wlr_texture *marks_urgent; struct { struct wl_signal destroy; @@ -159,29 +165,42 @@ void container_begin_destroy(struct sway_container *con); struct sway_container *container_find_child(struct sway_container *container, bool (*test)(struct sway_container *view, void *data), void *data); +/** + * Find a container at the given coordinates. Returns the the surface and + * surface-local coordinates of the given layout coordinates if the container + * is a view and the view contains a surface at those coordinates. + */ +struct sway_container *container_at(struct sway_workspace *workspace, + double lx, double ly, struct wlr_surface **surface, + double *sx, double *sy); + +struct sway_container *tiling_container_at( + struct sway_node *parent, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); + void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data); -/** - * Returns the fullscreen container obstructing this container if it exists. - */ -struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container); - /** * Returns true if the given container is an ancestor of this container. */ bool container_has_ancestor(struct sway_container *container, struct sway_container *ancestor); +void container_update_textures_recursive(struct sway_container *con); + +void container_damage_whole(struct sway_container *container); + void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); -void container_update_title_bar(struct sway_container *container); +void container_update_title_textures(struct sway_container *container); -void container_update_marks(struct sway_container *container); - -size_t parse_title_format(struct sway_container *container, char *buffer); +/** + * Calculate the container's title_height property. + */ +void container_calculate_title_height(struct sway_container *container); size_t container_build_representation(enum sway_container_layout layout, list_t *children, char *buffer); @@ -193,17 +212,10 @@ void container_update_representation(struct sway_container *container); */ size_t container_titlebar_height(void); -void floating_calculate_constraints(int *min_width, int *max_width, - int *min_height, int *max_height); - -void floating_fix_coordinates(struct sway_container *con, - struct wlr_box *old, struct wlr_box *new); - -void container_floating_resize_and_center(struct sway_container *con); - -void container_floating_set_default_size(struct sway_container *con); - -void container_set_resizing(struct sway_container *con, bool resizing); +/** + * Resize and center the container in its workspace. + */ +void container_init_floating(struct sway_container *container); void container_set_floating(struct sway_container *container, bool enable); @@ -212,8 +224,6 @@ void container_set_geometry_from_content(struct sway_container *con); /** * Determine if the given container is itself floating. * This will return false for any descendants of a floating container. - * - * Uses pending container state. */ bool container_is_floating(struct sway_container *container); @@ -260,13 +270,6 @@ void container_set_fullscreen(struct sway_container *con, */ void container_fullscreen_disable(struct sway_container *con); -/** - * Walk up the container tree branch starting at the given container, and return - * its earliest ancestor. - */ -struct sway_container *container_toplevel_ancestor( - struct sway_container *container); - /** * Return true if the container is floating, or a child of a floating split * container. @@ -279,12 +282,29 @@ bool container_is_floating_or_child(struct sway_container *container); */ bool container_is_fullscreen_or_child(struct sway_container *container); +/** + * Return the output which will be used for scale purposes. + * This is the most recently entered output. + */ +struct sway_output *container_get_effective_output(struct sway_container *con); + +void container_discover_outputs(struct sway_container *con); + +void container_remove_gaps(struct sway_container *container); + +void container_add_gaps(struct sway_container *container); + enum sway_container_layout container_parent_layout(struct sway_container *con); +enum sway_container_layout container_current_parent_layout( + struct sway_container *con); + list_t *container_get_siblings(struct sway_container *container); int container_sibling_index(struct sway_container *child); +list_t *container_get_current_siblings(struct sway_container *container); + void container_handle_fullscreen_reparent(struct sway_container *con); void container_add_child(struct sway_container *parent, @@ -304,8 +324,6 @@ void container_detach(struct sway_container *child); void container_replace(struct sway_container *container, struct sway_container *replacement); -void container_swap(struct sway_container *con1, struct sway_container *con2); - struct sway_container *container_split(struct sway_container *child, enum sway_container_layout layout); @@ -332,33 +350,10 @@ bool container_has_mark(struct sway_container *container, char *mark); void container_add_mark(struct sway_container *container, char *mark); +void container_update_marks_textures(struct sway_container *container); + void container_raise_floating(struct sway_container *con); bool container_is_scratchpad_hidden(struct sway_container *con); -bool container_is_scratchpad_hidden_or_child(struct sway_container *con); - -bool container_is_sticky(struct sway_container *con); - -bool container_is_sticky_or_child(struct sway_container *con); - -/** - * This will destroy pairs of redundant H/V splits - * e.g. H[V[H[app app]] app] -> H[app app app] - * The middle "V[H[" are eliminated by a call to container_squash - * on the V[ con. It's grandchildren are added to its parent. - * - * This function is roughly equivalent to i3's tree_flatten here: - * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 - * - * Returns the number of new containers added to the parent - */ -int container_squash(struct sway_container *con); - -void container_arrange_title_bar(struct sway_container *con); - -void container_update(struct sway_container *con); - -void container_update_itself_and_parents(struct sway_container *con); - #endif diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index e2dbcdf05..5b8c19093 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -1,13 +1,8 @@ #ifndef _SWAY_NODE_H #define _SWAY_NODE_H -#include #include -#include #include "list.h" -#define MIN_SANE_W 100 -#define MIN_SANE_H 60 - struct sway_root; struct sway_output; struct sway_workspace; @@ -76,15 +71,4 @@ list_t *node_get_children(struct sway_node *node); bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); -// when destroying a sway tree, it's not known which order the tree will be -// destroyed. To prevent freeing of scene_nodes recursing up the tree, -// let's use this helper function to disown them to the staging node. -void scene_node_disown_children(struct wlr_scene_tree *tree); - -// a helper function used to allocate tree nodes. If an allocation failure -// occurs a flag is flipped that can be checked later to destroy a parent -// of this scene node preventing memory leaks. -struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, - bool *failed); - #endif diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 10d9ea981..9ff45eb5f 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -2,12 +2,11 @@ #define _SWAY_ROOT_H #include #include -#include #include -#include #include #include "sway/tree/container.h" #include "sway/tree/node.h" +#include "config.h" #include "list.h" extern struct sway_root *root; @@ -16,43 +15,13 @@ struct sway_root { struct sway_node node; struct wlr_output_layout *output_layout; - // scene node layout: - // - root - // - staging - // - layer shell stuff - // - tiling - // - floating - // - fullscreen stuff - // - seat stuff - // - ext_session_lock - struct wlr_scene *root_scene; - - // since wlr_scene nodes can't be orphaned and must always - // have a parent, use this staging scene_tree so that a - // node always have a valid parent. Nothing in this - // staging node will be visible. - struct wlr_scene_tree *staging; - - // tree containing all layers the compositor will render. Cursor handling - // will end up iterating this tree. - struct wlr_scene_tree *layer_tree; - - struct { - struct wlr_scene_tree *shell_background; - struct wlr_scene_tree *shell_bottom; - struct wlr_scene_tree *tiling; - struct wlr_scene_tree *floating; - struct wlr_scene_tree *shell_top; - struct wlr_scene_tree *fullscreen; - struct wlr_scene_tree *fullscreen_global; -#if WLR_HAS_XWAYLAND - struct wlr_scene_tree *unmanaged; + struct wl_listener output_layout_change; +#if HAVE_XWAYLAND + struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link #endif - struct wlr_scene_tree *shell_overlay; - struct wlr_scene_tree *popup; - struct wlr_scene_tree *seat; - struct wlr_scene_tree *session_lock; - } layers; + 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 @@ -61,11 +30,10 @@ struct sway_root { double width, height; list_t *outputs; // struct sway_output - list_t *non_desktop_outputs; // struct sway_output_non_desktop list_t *scratchpad; // struct sway_container // For when there's no connected outputs - struct sway_output *fallback_output; + struct sway_output *noop_output; struct sway_container *fullscreen_global; @@ -74,18 +42,14 @@ struct sway_root { } events; }; -struct sway_root *root_create(struct wl_display *display); +struct sway_root *root_create(void); void root_destroy(struct sway_root *root); /** * Move a container to the scratchpad. - * If a workspace is passed, the container is assumed to have been in - * the scratchpad before and is shown on the workspace. - * The ws parameter can safely be NULL. */ -void root_scratchpad_add_container(struct sway_container *con, - struct sway_workspace *ws); +void root_scratchpad_add_container(struct sway_container *con); /** * Remove a container from the scratchpad. @@ -102,6 +66,10 @@ void root_scratchpad_show(struct sway_container *con); */ void root_scratchpad_hide(struct sway_container *con); +struct sway_workspace *root_workspace_for_pid(pid_t pid); + +void root_record_workspace_pid(pid_t pid); + void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), void *data); diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 26148a92a..5cc9777b4 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -1,12 +1,10 @@ #ifndef _SWAY_VIEW_H #define _SWAY_VIEW_H -#include -#include -#include -#include -#include -#include "sway/config.h" -#if WLR_HAS_XWAYLAND +#include +#include +#include +#include "config.h" +#if HAVE_XWAYLAND #include #endif #include "sway/input/input-manager.h" @@ -16,8 +14,9 @@ struct sway_container; struct sway_xdg_decoration; enum sway_view_type { + SWAY_VIEW_XDG_SHELL_V6, SWAY_VIEW_XDG_SHELL, -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND SWAY_VIEW_XWAYLAND, #endif }; @@ -25,23 +24,16 @@ enum sway_view_type { enum sway_view_prop { VIEW_PROP_TITLE, VIEW_PROP_APP_ID, - VIEW_PROP_TAG, VIEW_PROP_CLASS, VIEW_PROP_INSTANCE, VIEW_PROP_WINDOW_TYPE, VIEW_PROP_WINDOW_ROLE, -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND VIEW_PROP_X11_WINDOW_ID, VIEW_PROP_X11_PARENT_ID, #endif }; -enum sway_view_tearing_mode { - TEARING_OVERRIDE_FALSE, - TEARING_OVERRIDE_TRUE, - TEARING_WINDOW_HINT, -}; - struct sway_view_impl { void (*get_constraints)(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height); @@ -53,8 +45,11 @@ struct sway_view_impl { void (*set_activated)(struct sway_view *view, bool activated); void (*set_tiled)(struct sway_view *view, bool tiled); void (*set_fullscreen)(struct sway_view *view, bool fullscreen); - void (*set_resizing)(struct sway_view *view, bool resizing); bool (*wants_floating)(struct sway_view *view); + void (*for_each_surface)(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); + void (*for_each_popup)(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); bool (*is_transient_for)(struct sway_view *child, struct sway_view *ancestor); void (*close)(struct sway_view *view); @@ -66,74 +61,61 @@ struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; - struct wlr_scene_tree *scene_tree; - struct wlr_scene_tree *content_tree; - struct wlr_scene_tree *saved_surface_tree; - struct wlr_scene_buffer *output_handler; - - struct wl_listener outputs_update; - - struct wlr_scene *image_capture_scene; - struct wlr_ext_image_capture_source_v1 *image_capture_source; - struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views struct sway_xdg_decoration *xdg_decoration; pid_t pid; - struct launcher_ctx *ctx; + + double saved_x, saved_y; + int saved_width, saved_height; // The size the view would want to be if it weren't tiled. // Used when changing a view from tiled to floating. int natural_width, natural_height; + char *title_format; + bool using_csd; struct timespec urgent; bool allow_request_urgent; struct wl_event_source *urgent_timer; + struct wlr_buffer *saved_buffer; + int saved_buffer_width, saved_buffer_height; + // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. struct wlr_box geometry; - struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; - - struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; - struct wl_listener foreign_activate_request; - struct wl_listener foreign_fullscreen_request; - struct wl_listener foreign_close_request; - struct wl_listener foreign_destroy; + // The "old" geometry during a transaction. Used to damage the old location + // when a transaction is applied. + struct wlr_box saved_geometry; bool destroying; list_t *executed_criteria; // struct criteria * union { - struct wlr_xdg_toplevel *wlr_xdg_toplevel; -#if WLR_HAS_XWAYLAND + struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; + struct wlr_xdg_surface *wlr_xdg_surface; +#if HAVE_XWAYLAND struct wlr_xwayland_surface *wlr_xwayland_surface; #endif + struct wlr_wl_shell_surface *wlr_wl_shell_surface; }; struct { struct wl_signal unmap; } events; - int max_render_time; // In milliseconds - - enum seat_config_shortcuts_inhibit shortcuts_inhibit; - - enum sway_view_tearing_mode tearing_mode; - enum wp_tearing_control_v1_presentation_hint tearing_hint; + struct wl_listener surface_new_subsurface; }; -struct sway_xdg_shell_view { +struct sway_xdg_shell_v6_view { struct sway_view view; - struct wlr_scene_tree *image_capture_tree; - char *tag; - struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -146,76 +128,103 @@ struct sway_xdg_shell_view { struct wl_listener unmap; struct wl_listener destroy; }; -#if WLR_HAS_XWAYLAND -struct sway_xwayland_view { + +struct sway_xdg_shell_view { + struct sway_view view; + + struct wl_listener commit; + struct wl_listener request_move; + struct wl_listener request_resize; + struct wl_listener request_maximize; + struct wl_listener request_fullscreen; + struct wl_listener set_title; + struct wl_listener set_app_id; + struct wl_listener new_popup; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; +}; +#if HAVE_XWAYLAND +struct sway_xwayland_view { struct sway_view view; - struct wlr_scene_tree *surface_tree; - - struct wlr_scene_surface *image_capture_scene_surface; - struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; struct wl_listener request_maximize; - struct wl_listener request_minimize; struct wl_listener request_configure; struct wl_listener request_fullscreen; struct wl_listener request_activate; struct wl_listener set_title; struct wl_listener set_class; struct wl_listener set_role; - struct wl_listener set_startup_id; struct wl_listener set_window_type; struct wl_listener set_hints; struct wl_listener set_decorations; - struct wl_listener associate; - struct wl_listener dissociate; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - struct wl_listener override_redirect; - - struct wl_listener surface_tree_destroy; }; struct sway_xwayland_unmanaged { struct wlr_xwayland_surface *wlr_xwayland_surface; + struct wl_list link; - struct wlr_scene_surface *surface_scene; + int lx, ly; - struct wl_listener request_activate; struct wl_listener request_configure; struct wl_listener request_fullscreen; - struct wl_listener set_geometry; - struct wl_listener associate; - struct wl_listener dissociate; + struct wl_listener commit; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - struct wl_listener override_redirect; }; #endif +struct sway_view_child; + +struct sway_view_child_impl { + void (*get_root_coords)(struct sway_view_child *child, int *sx, int *sy); + void (*destroy)(struct sway_view_child *child); +}; + +/** + * A view child is a surface in the view tree, such as a subsurface or a popup. + */ +struct sway_view_child { + const struct sway_view_child_impl *impl; -struct sway_popup_desc { - struct wlr_scene_node *relative; struct sway_view *view; + struct wlr_surface *surface; + bool mapped; + + struct wl_listener surface_commit; + struct wl_listener surface_new_subsurface; + struct wl_listener surface_map; + struct wl_listener surface_unmap; + struct wl_listener surface_destroy; +}; + +struct sway_subsurface { + struct sway_view_child child; + + struct wl_listener destroy; +}; + +struct sway_xdg_popup_v6 { + struct sway_view_child child; + + struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; + + struct wl_listener new_popup; + struct wl_listener destroy; }; struct sway_xdg_popup { - struct sway_view *view; - struct wlr_xdg_popup *wlr_xdg_popup; + struct sway_view_child child; - struct wlr_scene_tree *scene_tree; - struct wlr_scene_tree *xdg_surface_tree; + struct wlr_xdg_surface *wlr_xdg_surface; - struct wlr_scene_tree *image_capture_tree; - - struct sway_popup_desc desc; - - struct wl_listener surface_commit; struct wl_listener new_popup; - struct wl_listener reposition; struct wl_listener destroy; }; @@ -235,14 +244,6 @@ const char *view_get_window_role(struct sway_view *view); uint32_t view_get_window_type(struct sway_view *view); -const char *view_get_sandbox_engine(struct sway_view *view); - -const char *view_get_sandbox_app_id(struct sway_view *view); - -const char *view_get_sandbox_instance_id(struct sway_view *view); - -const char *view_get_tag(struct sway_view *view); - const char *view_get_shell(struct sway_view *view); void view_get_constraints(struct sway_view *view, double *min_width, @@ -251,15 +252,12 @@ void view_get_constraints(struct sway_view *view, double *min_width, uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); -bool view_inhibit_idle(struct sway_view *view); - /** - * Whether or not this view's most distant ancestor (possibly itself) is the - * only visible node in its tree. If the view is tiling, there may be floating - * views. If the view is floating, there may be tiling views or views in a - * different floating container. + * Whether or not the view is the only visible view in its tree. If the view + * is tiling, there may be floating views. If the view is floating, there may + * be tiling views or views in a different floating container. */ -bool view_ancestor_is_only_visible(struct sway_view *view); +bool view_is_only_visible(struct sway_view *view); /** * Configure the view's position and size based on the container's position and @@ -272,12 +270,7 @@ void view_set_activated(struct sway_view *view, bool activated); /** * Called when the view requests to be focused. */ -void view_request_activate(struct sway_view *view, struct sway_seat *seat); - -/* - * Called when the view requests urgent state - */ -void view_request_urgent(struct sway_view *view); +void view_request_activate(struct sway_view *view); /** * If possible, instructs the client to change their decoration mode. @@ -296,42 +289,53 @@ void view_close(struct sway_view *view); void view_close_popups(struct sway_view *view); +void view_damage_from(struct sway_view *view); + +/** + * Iterate all surfaces of a view (toplevels + popups). + */ +void view_for_each_surface(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); + +/** + * Iterate all popups recursively. + */ +void view_for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); + // view implementation -bool view_init(struct sway_view *view, enum sway_view_type type, +void view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl); void view_destroy(struct sway_view *view); void view_begin_destroy(struct sway_view *view); -/** - * Map a view, ie. make it visible in the tree. - * - * `fullscreen` should be set to true (and optionally `fullscreen_output` - * should be populated) if the view should be made fullscreen immediately. - * - * `decoration` should be set to true if the client prefers CSD. The client's - * preference may be ignored. - */ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, - bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); + bool fullscreen, bool decoration); void view_unmap(struct sway_view *view); -void view_update_size(struct sway_view *view); -void view_center_and_clip_surface(struct sway_view *view); +void view_update_size(struct sway_view *view, int width, int height); + +void view_child_init(struct sway_view_child *child, + const struct sway_view_child_impl *impl, struct sway_view *view, + struct wlr_surface *surface); + +void view_child_destroy(struct sway_view_child *child); + struct sway_view *view_from_wlr_xdg_surface( struct wlr_xdg_surface *xdg_surface); -#if WLR_HAS_XWAYLAND +struct sway_view *view_from_wlr_xdg_surface_v6( + struct wlr_xdg_surface_v6 *xdg_surface_v6); +#if HAVE_XWAYLAND struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface); #endif struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); -void view_update_app_id(struct sway_view *view); - /** * Re-read the view's title property and update any relevant title bars. * The force argument makes it recreate the title bars even if the title hasn't @@ -361,12 +365,4 @@ void view_save_buffer(struct sway_view *view); bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); -void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); - -void view_send_frame_done(struct sway_view *view); - -bool view_can_tear(struct sway_view *view); - -void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data); - #endif diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 50bf02cd1..7abfbff13 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -2,9 +2,6 @@ #define _SWAY_WORKSPACE_H #include -#include -#include -#include "sway/config.h" #include "sway/tree/container.h" #include "sway/tree/node.h" @@ -25,12 +22,6 @@ struct sway_workspace_state { struct sway_workspace { struct sway_node node; - - struct { - struct wlr_scene_tree *tiling; - struct wlr_scene_tree *fullscreen; - } layers; - struct sway_container *fullscreen; char *name; @@ -52,7 +43,6 @@ struct sway_workspace { bool urgent; struct sway_workspace_state current; - struct wlr_ext_workspace_handle_v1 *ext_workspace; // Always set. }; struct workspace_config *workspace_find_config(const char *ws_name); @@ -70,10 +60,8 @@ void workspace_consider_destroy(struct sway_workspace *ws); char *workspace_next_name(const char *output_name); -struct sway_workspace *workspace_auto_back_and_forth( - struct sway_workspace *workspace); - -bool workspace_switch(struct sway_workspace *workspace); +bool workspace_switch(struct sway_workspace *workspace, + bool no_auto_back_and_forth); struct sway_workspace *workspace_by_number(const char* name); @@ -98,7 +86,7 @@ void workspace_output_add_priority(struct sway_workspace *workspace, struct sway_output *output); struct sway_output *workspace_output_get_highest_available( - struct sway_workspace *ws); + struct sway_workspace *ws, struct sway_output *exclude); void workspace_detect_urgent(struct sway_workspace *workspace); @@ -115,25 +103,15 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws, */ struct sway_container *workspace_wrap_children(struct sway_workspace *ws); -void workspace_unwrap_children(struct sway_workspace *ws, - struct sway_container *wrap); - void workspace_detach(struct sway_workspace *workspace); -struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, +void workspace_add_tiling(struct sway_workspace *workspace, struct sway_container *con); void workspace_add_floating(struct sway_workspace *workspace, struct sway_container *con); -/** - * Adds a tiling container to the workspace without considering - * the workspace_layout, so the con will not be split. - */ -void workspace_insert_tiling_direct(struct sway_workspace *workspace, - struct sway_container *con, int index); - -struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, +void workspace_insert_tiling(struct sway_workspace *workspace, struct sway_container *con, int index); void workspace_remove_gaps(struct sway_workspace *ws); @@ -149,21 +127,4 @@ void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box); size_t workspace_num_tiling_views(struct sway_workspace *ws); -size_t workspace_num_sticky_containers(struct sway_workspace *ws); - -/** - * workspace_squash is container_flatten in the reverse - * direction. Instead of eliminating redundant splits that are - * parents of the target container, it eliminates pairs of - * redundant H/V splits that are children of the workspace. - */ -void workspace_squash(struct sway_workspace *workspace); - -void workspace_move_to_output(struct sway_workspace *workspace, - struct sway_output *output); - -bool sway_ext_workspace_init(void); -void sway_ext_workspace_output_enable(struct sway_output *output); -void sway_ext_workspace_output_disable(struct sway_output *output); - #endif diff --git a/include/sway/xdg_decoration.h b/include/sway/xdg_decoration.h index 2388ebcbb..8bef4c6d4 100644 --- a/include/sway/xdg_decoration.h +++ b/include/sway/xdg_decoration.h @@ -16,6 +16,4 @@ struct sway_xdg_decoration { struct sway_xdg_decoration *xdg_decoration_from_surface( struct wlr_surface *surface); -void set_xdg_decoration_mode(struct sway_xdg_decoration *deco); - #endif diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 197d21901..2518d5aa8 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -4,7 +4,6 @@ #include "config.h" #include "input.h" #include "pool-buffer.h" -#include "cursor-shape-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" @@ -24,17 +23,17 @@ 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; struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; - struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_shm *shm; + struct wl_seat *seat; struct swaybar_config *config; + struct swaybar_pointer pointer; struct status_line *status; struct loop *eventloop; @@ -43,8 +42,6 @@ struct swaybar { int ipc_socketfd; struct wl_list outputs; // swaybar_output::link - struct wl_list unused_outputs; // swaybar_output::link - struct wl_list seats; // swaybar_seat::link #if HAVE_TRAY struct swaybar_tray *tray; @@ -111,8 +108,4 @@ void set_bar_dirty(struct swaybar *bar); bool determine_bar_visibility(struct swaybar *bar, bool moving_layer); void free_workspaces(struct wl_list *list); -void status_in(int fd, short mask, void *data); - -void destroy_layer_surface(struct swaybar_output *output); - #endif diff --git a/include/swaybar/config.h b/include/swaybar/config.h index ad58b3c3c..ec042e514 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -6,7 +6,6 @@ #include "../include/config.h" #include "list.h" #include "util.h" -#include struct box_colors { uint32_t border; @@ -14,14 +13,10 @@ struct box_colors { uint32_t text; }; -struct box_size { - uint32_t width; - uint32_t height; -}; - struct config_output { struct wl_list link; // swaybar_config::outputs char *name; + size_t index; }; struct swaybar_binding { @@ -34,7 +29,7 @@ struct swaybar_config { char *status_command; bool pango_markup; uint32_t position; // zwlr_layer_surface_v1_anchor - PangoFontDescription *font_description; + char *font; char *sep_symbol; char *mode; char *hidden_state; @@ -44,9 +39,9 @@ struct swaybar_config { bool binding_mode_indicator; bool wrap_scroll; bool workspace_buttons; - uint32_t workspace_min_width; list_t *bindings; struct wl_list outputs; // config_output::link + bool all_outputs; int height; int status_padding; int status_edge_padding; @@ -88,13 +83,10 @@ struct tray_binding { char *command; struct wl_list link; // struct tray_binding::link }; - -void free_tray_binding(struct tray_binding *binding); #endif struct swaybar_config *init_config(void); void free_config(struct swaybar_config *config); uint32_t parse_position(const char *position); -void free_binding(struct swaybar_binding *binding); #endif diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h index dced2a6cd..5b6001ceb 100644 --- a/include/swaybar/i3bar.h +++ b/include/swaybar/i3bar.h @@ -9,8 +9,7 @@ struct i3bar_block { int ref_count; char *full_text, *short_text, *align, *min_width_str; bool urgent; - uint32_t color; - bool color_set; + uint32_t *color; int min_width; char *name, *instance; bool separator; @@ -19,7 +18,6 @@ struct i3bar_block { // Airblader features uint32_t background; uint32_t border; - bool border_set; int border_top; int border_bottom; int border_left; @@ -29,7 +27,7 @@ struct i3bar_block { void i3bar_block_unref(struct i3bar_block *block); bool i3bar_handle_readable(struct status_line *status); enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, - struct i3bar_block *block, double x, double y, double rx, double ry, - double w, double h, int scale, uint32_t button, bool released); + struct i3bar_block *block, int x, int y, int rx, int ry, int w, int h, + uint32_t button); #endif diff --git a/include/swaybar/image.h b/include/swaybar/image.h deleted file mode 100644 index 53a210dd7..000000000 --- a/include/swaybar/image.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _SWAYBAR_IMAGE_H -#define _SWAYBAR_IMAGE_H -#include - -cairo_surface_t *load_image(const char *path); - -#endif diff --git a/include/swaybar/input.h b/include/swaybar/input.h index 8ea88a69a..d76cd551f 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -2,7 +2,6 @@ #define _SWAYBAR_INPUT_H #include -#include #include "list.h" #define SWAY_SCROLL_UP KEY_MAX + 1 @@ -10,9 +9,6 @@ #define SWAY_SCROLL_LEFT KEY_MAX + 3 #define SWAY_SCROLL_RIGHT KEY_MAX + 4 -#define SWAY_CONTINUOUS_SCROLL_TIMEOUT 1000 -#define SWAY_CONTINUOUS_SCROLL_THRESHOLD 10000 - struct swaybar; struct swaybar_output; @@ -22,23 +18,10 @@ struct swaybar_pointer { struct wl_cursor_image *cursor_image; struct wl_surface *cursor_surface; struct swaybar_output *current; - double x, y; + int x, y; 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, @@ -48,36 +31,18 @@ struct swaybar_hotspot { struct wl_list link; // swaybar_output::hotspots int x, y, width, height; enum hotspot_event_handling (*callback)(struct swaybar_output *output, - struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, - bool released, void *data); + struct swaybar_hotspot *hotspot, int x, int y, uint32_t button, + void *data); void (*destroy)(void *data); void *data; }; -struct swaybar_scroll_axis { - wl_fixed_t value; - uint32_t discrete_steps; - uint32_t update_time; -}; - -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 - struct swaybar_scroll_axis axis[2]; -}; - extern const struct wl_seat_listener seat_listener; -void update_cursor(struct swaybar_seat *seat); +void update_cursor(struct swaybar *bar); 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/swaybar/status_line.h b/include/swaybar/status_line.h index 65c3a7969..3601a11e8 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -28,7 +28,6 @@ struct status_line { int cont_signal; bool click_events; - bool float_event_coords; bool clicked; char *buffer; size_t buffer_size; diff --git a/include/swaybar/tray/icon.h b/include/swaybar/tray/icon.h index 3673674b9..7a6c400cb 100644 --- a/include/swaybar/tray/icon.h +++ b/include/swaybar/tray/icon.h @@ -3,16 +3,16 @@ #include "list.h" +enum subdir_type { + THRESHOLD, + SCALABLE, + FIXED +}; + struct icon_theme_subdir { char *name; int size; - - enum { - THRESHOLD, - SCALABLE, - FIXED - } type; - + enum subdir_type type; int max_size; int min_size; int threshold; @@ -21,7 +21,7 @@ struct icon_theme_subdir { struct icon_theme { char *name; char *comment; - list_t *inherits; // char * + char *inherits; list_t *directories; // char * char *dir; @@ -39,5 +39,6 @@ void finish_themes(list_t *themes, list_t *basedirs); */ char *find_icon(list_t *themes, list_t *basedirs, char *name, int size, char *theme, int *min_size, int *max_size); +char *find_icon_in_dir(char *name, char *dir, int *min_size, int *max_size); #endif diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index 73937a0cc..4238bdb8e 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h @@ -4,7 +4,6 @@ #include #include #include -#include #include "swaybar/tray/tray.h" #include "list.h" @@ -15,22 +14,12 @@ struct swaybar_pixmap { unsigned char pixels[]; }; -struct swaybar_sni_slot { - struct wl_list link; // swaybar_sni::slots - struct swaybar_sni *sni; - const char *prop; - const char *type; - void *dest; - sd_bus_slot *slot; -}; - struct swaybar_sni { // icon properties struct swaybar_tray *tray; cairo_surface_t *icon; int min_size; int max_size; - int target_size; // dbus properties char *watcher_id; @@ -47,7 +36,9 @@ struct swaybar_sni { char *menu; char *icon_theme_path; // non-standard KDE property - struct wl_list slots; // swaybar_sni_slot::link + sd_bus_slot *new_icon_slot; + sd_bus_slot *new_attention_icon_slot; + sd_bus_slot *new_status_slot; }; struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h index d2e80a6d4..8958b69aa 100644 --- a/include/swaybar/tray/tray.h +++ b/include/swaybar/tray/tray.h @@ -2,12 +2,10 @@ #define _SWAYBAR_TRAY_TRAY_H #include "config.h" -#if HAVE_LIBSYSTEMD +#ifdef HAVE_SYSTEMD #include -#elif HAVE_LIBELOGIND +#elif HAVE_ELOGIND #include -#elif HAVE_BASU -#include #endif #include #include diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index fb9e9c218..0fd1eb50a 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -4,9 +4,8 @@ #include #include "list.h" #include "pool-buffer.h" -#include "cursor-shape-v1-client-protocol.h" - #include "swaynag/types.h" +#include "xdg-output-unstable-v1-client-protocol.h" #define SWAYNAG_MAX_HEIGHT 500 @@ -28,14 +27,6 @@ struct swaynag_pointer { int y; }; -struct swaynag_seat { - struct wl_seat *wl_seat; - uint32_t wl_name; - struct swaynag *swaynag; - struct swaynag_pointer pointer; - struct wl_list link; -}; - struct swaynag_output { char *name; struct wl_output *wl_output; @@ -54,13 +45,11 @@ struct swaynag_button { int width; int height; bool terminal; - bool dismiss; }; struct swaynag_details { bool visible; char *message; - char *details_text; int x; int y; @@ -77,17 +66,18 @@ struct swaynag_details { struct swaynag { bool run_display; + int querying_outputs; struct wl_display *display; struct wl_compositor *compositor; struct wl_seat *seat; struct wl_shm *shm; + struct swaynag_pointer pointer; + struct zxdg_output_manager_v1 *xdg_output_manager; struct wl_list outputs; // swaynag_output::link - struct wl_list seats; // swaynag_seat::link struct swaynag_output *output; struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_surface_v1 *layer_surface; - struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_surface *surface; uint32_t width; diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 9c3c50db4..2183ce224 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -1,40 +1,29 @@ #ifndef _SWAYNAG_TYPES_H #define _SWAYNAG_TYPES_H -#include -#include -#include "list.h" - struct swaynag_type { char *name; - PangoFontDescription *font_description; + char *font; char *output; uint32_t anchors; - int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset - // Colors - uint32_t button_text; uint32_t button_background; - uint32_t details_background; uint32_t background; uint32_t text; uint32_t border; uint32_t border_bottom; - // 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; + 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; }; -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 92f5916cf..1fd772c0c 100644 --- a/include/util.h +++ b/include/util.h @@ -3,45 +3,17 @@ #include #include -#include - -enum movement_unit { - MOVEMENT_UNIT_PX, - MOVEMENT_UNIT_PPT, - MOVEMENT_UNIT_DEFAULT, - MOVEMENT_UNIT_INVALID, -}; - -struct movement_amount { - int amount; - enum movement_unit unit; -}; - -/* - * Parse units such as "px" or "ppt" - */ -enum movement_unit parse_movement_unit(const char *unit); - -/* - * Parse arguments such as "10", "10px" or "10 px". - * Returns the number of arguments consumed. - */ -int parse_movement_amount(int argc, char **argv, - struct movement_amount *amount); /** - * Wrap i into the range [0, max] + * Wrap i into the range [0, max[ */ int wrap(int i, int max); /** - * Given a string that represents an RGB(A) color, result will be set to a - * uint32_t version of the color, as long as it is valid. If it is invalid, - * then false will be returned and result will be untouched. + * Given a string that represents an RGB(A) color, return a uint32_t + * version of the color. */ -bool parse_color(const char *color, uint32_t *result); - -void color_to_rgba(float dest[static 4], uint32_t color); +uint32_t parse_color(const char *color); /** * Given a string that represents a boolean, return the boolean value. This @@ -57,10 +29,4 @@ 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 sway_set_cloexec(int fd, bool cloexec); - -uint32_t get_current_time_in_msec(void); - #endif diff --git a/meson.build b/meson.build index 17d65c334..d3172bdd8 100644 --- a/meson.build +++ b/meson.build @@ -1,26 +1,25 @@ project( 'sway', 'c', - version: '1.13-dev', + version: '1.0', license: 'MIT', - meson_version: '>=1.3', + meson_version: '>=0.48.0', default_options: [ 'c_std=c11', 'warning_level=2', 'werror=true', - 'wrap_mode=nodownload', ], ) add_project_arguments( [ + '-DSWAY_SRC_DIR="@0@"'.format(meson.current_source_dir()), + + '-DWL_HIDE_DEPRECATED', '-DWLR_USE_UNSTABLE', - '-D_POSIX_C_SOURCE=200809L', '-Wno-unused-parameter', '-Wno-unused-result', - '-Wno-missing-braces', - '-Wno-format-zero-length', '-Wundef', '-Wvla', ], @@ -38,104 +37,81 @@ if is_freebsd add_project_arguments('-D_C11_SOURCE', language: 'c') endif -# Execute the wlroots subproject, if any -wlroots_version = ['>=0.21.0', '<0.22.0'] -subproject( - 'wlroots', - default_options: ['examples=false'], - required: false, - version: wlroots_version, -) -wlroots = dependency('wlroots-0.21', version: wlroots_version, fallback: 'wlroots') -wlroots_features = { - 'xwayland': false, - 'libinput_backend': false, - 'session': false, -} -foreach name, _ : wlroots_features - var_name = 'have_' + name.underscorify() - have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true' - wlroots_features += { name: have } -endforeach - -null_dep = dependency('', required: false) - -jsonc = dependency('json-c', version: '>=0.13') -pcre2 = dependency('libpcre2-8') -wayland_server = dependency('wayland-server', version: '>=1.21.0') +jsonc = dependency('json-c', version: '>=0.13') +pcre = dependency('libpcre') +wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') -wayland_protos = dependency('wayland-protocols', version: '>=1.41', default_options: ['tests=false']) -xkbcommon = dependency('xkbcommon', version: '>=1.5.0') -cairo = dependency('cairo') -pango = dependency('pango') -pangocairo = dependency('pangocairo') -gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) -pixman = dependency('pixman-1') -libevdev = dependency('libevdev') -libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.26.0') : null_dep -xcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep -drm = dependency('libdrm') -libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep -math = cc.find_library('m') -rt = cc.find_library('rt') -xcb_icccm = wlroots_features['xwayland'] ? dependency('xcb-icccm') : null_dep -threads = dependency('threads') # for pthread_setschedparam and pthread_atfork +wayland_egl = dependency('wayland-egl') +wayland_protos = dependency('wayland-protocols', version: '>=1.14') +xkbcommon = dependency('xkbcommon') +cairo = dependency('cairo') +pango = dependency('pango') +pangocairo = dependency('pangocairo') +gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) +pixman = dependency('pixman-1') +libevdev = dependency('libevdev') +libinput = dependency('libinput', version: '>=1.6.0') +systemd = dependency('libsystemd', version: '>=239', required: false) +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) -if get_option('sd-bus-provider') == 'auto' - if not get_option('tray').disabled() - assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') - endif - sdbus = dependency(['libsystemd', 'libelogind'], - required: false, - version: '>=239', - ) - if not sdbus.found() - sdbus = dependency('basu', required: false) - endif +# Try first to find wlroots as a subproject, then as a system dependency +wlroots_proj = subproject( + 'wlroots', + default_options: ['rootston=false', 'examples=false'], + required: false, +) +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 - sdbus = dependency(get_option('sd-bus-provider'), required: get_option('tray')) + wlroots = dependency('wlroots') + wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include ', dependencies: wlroots) == '1' endif -tray_deps_found = sdbus.found() +if get_option('xwayland').enabled() and not wlroots_has_xwayland + error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') +endif +have_xwayland = xcb.found() and wlroots_has_xwayland + +tray_deps_found = systemd.found() or elogind.found() if get_option('tray').enabled() and not tray_deps_found - error('Building with -Dtray=enabled, but sd-bus has not been not found') + error('Building with -Dtray=enabled, but libsystemd and libelogind have not been not found') endif have_tray = (not get_option('tray').disabled()) and tray_deps_found conf_data = configuration_data() +conf_data.set10('HAVE_XWAYLAND', have_xwayland) conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found()) -conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd') -conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') -conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') +conf_data.set10('HAVE_SYSTEMD', systemd.found()) +conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY', 'LIBINPUT_SWITCH_KEYPAD_SLIDE'] - conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput)) -endforeach -scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) +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')) if scdoc.found() - scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) + sh = find_program('sh') 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', ] - - if get_option('swaynag') - man_files += [ - 'swaynag/swaynag.1.scd', - 'swaynag/swaynag.5.scd', - ] - endif - foreach filename : man_files topic = filename.split('.')[-3].split('/')[-1] section = filename.split('.')[-2] @@ -145,10 +121,10 @@ if scdoc.found() output, input: filename, output: output, - command: scdoc_prog, + command: [ + sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.path(), output) + ], install: true, - feed: true, - capture: true, install_dir: '@0@/man@1@'.format(mandir, section) ) endforeach @@ -157,37 +133,15 @@ endif add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') version = '"@0@"'.format(meson.project_version()) -git = find_program('git', native: true, required: false) if git.found() - git_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false) - git_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) - if git_commit.returncode() == 0 and git_branch.returncode() == 0 - version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( - meson.project_version(), - git_commit.stdout().strip(), - git_branch.stdout().strip(), - ) + git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']) + git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']) + if git_commit_hash.returncode() == 0 and git_branch.returncode() == 0 + version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash.stdout().strip(), git_branch.stdout().strip()) endif endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') -fs = import('fs') - -# Strip relative path prefixes from the code if possible, otherwise hide them. -relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) + '/' -if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=') - add_project_arguments( - '-fmacro-prefix-map=@0@='.format(relative_dir), - language: 'c', - ) -else - add_project_arguments( - '-DSWAY_REL_SRC_DIR="@0@"'.format(relative_dir), - language: 'c', - ) -endif - - sway_inc = include_directories('include') subdir('include') @@ -196,15 +150,10 @@ subdir('common') subdir('sway') subdir('swaymsg') -if get_option('swaybar') or get_option('swaynag') - subdir('client') -endif -if get_option('swaybar') - subdir('swaybar') -endif -if get_option('swaynag') - subdir('swaynag') -endif +subdir('client') +subdir('swaybg') +subdir('swaybar') +subdir('swaynag') config = configuration_data() config.set('datadir', join_paths(prefix, datadir)) @@ -215,12 +164,28 @@ configure_file( configuration: config, input: 'config.in', output: '@BASENAME@', - install_dir: join_paths(sysconfdir, 'sway') + install_dir: sysconfdir + '/sway' ) +if is_freebsd + configure_file( + configuration: config, + input: 'security.d/10-freebsd.in', + output: '@BASENAME@', + install_dir: sysconfdir + '/sway/security.d' + ) +else + configure_file( + configuration: config, + input: 'security.d/00-defaults.in', + output: '@BASENAME@', + install_dir: sysconfdir + '/sway/security.d' + ) +endif + install_data( 'sway.desktop', - install_dir: join_paths(datadir, 'wayland-sessions') + install_dir: datadir + '/wayland-sessions' ) if get_option('default-wallpaper') @@ -234,15 +199,39 @@ if get_option('default-wallpaper') 'assets/Sway_Wallpaper_Blue_2048x1536.png', 'assets/Sway_Wallpaper_Blue_2048x1536_Portrait.png', ) - wallpaper_install_dir = join_paths(datadir, 'backgrounds', 'sway') + wallpaper_install_dir = datadir + '/backgrounds/sway' install_data(wallpaper_files, install_dir: wallpaper_install_dir) endif -subdir('completions') +if get_option('zsh-completions') + zsh_files = files( + 'completions/zsh/_sway', + 'completions/zsh/_swaymsg', + ) + zsh_install_dir = datadir + '/zsh/site-functions' -summary({ - 'gdk-pixbuf': gdk_pixbuf.found(), - 'tray': have_tray, - 'man-pages': scdoc.found(), -}, bool_yn: true) + install_data(zsh_files, install_dir: zsh_install_dir) +endif + +if get_option('bash-completions') + bash_files = files( + 'completions/bash/sway', + 'completions/bash/swaybar', + 'completions/bash/swaymsg', + ) + bash_install_dir = datadir + '/bash-completion/completions' + + install_data(bash_files, install_dir: bash_install_dir) +endif + +if get_option('fish-completions') + fish_files = files( + 'completions/fish/sway.fish', + 'completions/fish/swaymsg.fish', + 'completions/fish/swaynag.fish', + ) + fish_install_dir = datadir + '/fish/completions' + + install_data(fish_files, install_dir: fish_install_dir) +endif diff --git a/meson_options.txt b/meson_options.txt index 506ecc9a6..d3667acfd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,9 +2,7 @@ option('default-wallpaper', type: 'boolean', value: true, description: 'Install option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') -option('swaybar', type: 'boolean', value: true, description: 'Enable support for swaybar') -option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') +option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') -option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') +option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') -option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') diff --git a/protocols/idle.xml b/protocols/idle.xml new file mode 100644 index 000000000..92d9989c7 --- /dev/null +++ b/protocols/idle.xml @@ -0,0 +1,49 @@ + + + . + ]]> + + + This interface allows to monitor user idle time on a given seat. The interface + allows to register timers which trigger after no user activity was registered + on the seat for a given interval. It notifies when user activity resumes. + + This is useful for applications wanting to perform actions when the user is not + interacting with the system, e.g. chat applications setting the user as away, power + management features to dim screen, etc.. + + + + + + + + + + + + + + + + + + + + + + diff --git a/protocols/meson.build b/protocols/meson.build index a1cb35344..c438b078f 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -1,39 +1,85 @@ -wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') +wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') -wayland_scanner_dep = dependency('wayland-scanner', native: true) -wayland_scanner = find_program( - wayland_scanner_dep.get_variable('wayland_scanner'), - native: true, +wayland_scanner = find_program('wayland-scanner') + +# should check wayland_scanner's version, but it is hard to get +if wayland_server.version().version_compare('>=1.14.91') + code_type = 'private-code' +else + code_type = 'code' +endif + +wayland_scanner_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: [code_type, '@INPUT@', '@OUTPUT@'], ) -protocols = [ - wl_protocol_dir / 'stable/tablet/tablet-v2.xml', - wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', - wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', - wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', - 'wlr-layer-shell-unstable-v1.xml', - 'wlr-output-power-management-unstable-v1.xml', +wayland_scanner_client = generator( + wayland_scanner, + output: '@BASENAME@-client-protocol.h', + arguments: ['client-header', '@INPUT@', '@OUTPUT@'], +) + +wayland_scanner_server = generator( + wayland_scanner, + output: '@BASENAME@-protocol.h', + arguments: ['server-header', '@INPUT@', '@OUTPUT@'], +) + +client_protocols = [ + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], + ['wlr-layer-shell-unstable-v1.xml'], + ['idle.xml'], + ['wlr-input-inhibitor-unstable-v1.xml'], ] -wl_protos_src = [] +server_protocols = [ + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], + [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], + ['wlr-layer-shell-unstable-v1.xml'], + ['wlr-input-inhibitor-unstable-v1.xml'], +] -foreach xml : protocols - wl_protos_src += custom_target( - xml.underscorify() + '_c', - input: xml, - output: '@BASENAME@-protocol.c', - command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], - ) - wl_protos_src += custom_target( - xml.underscorify() + '_server_h', - input: xml, - output: '@BASENAME@-protocol.h', - command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], - ) - wl_protos_src += custom_target( - xml.underscorify() + '_client_h', - input: xml, - output: '@BASENAME@-client-protocol.h', - command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], - ) +client_protos_src = [] +client_protos_headers = [] + +server_protos_src = [] +server_protos_headers = [] + +foreach p : client_protocols + xml = join_paths(p) + client_protos_src += wayland_scanner_code.process(xml) + client_protos_headers += wayland_scanner_client.process(xml) endforeach + +foreach p : server_protocols + xml = join_paths(p) + server_protos_src += wayland_scanner_code.process(xml) + server_protos_headers += wayland_scanner_server.process(xml) +endforeach + +lib_client_protos = static_library( + 'client_protos', + client_protos_src + client_protos_headers, + dependencies: [wayland_client] +) # for the include directory + +client_protos = declare_dependency( + link_with: lib_client_protos, + sources: client_protos_headers, +) + +lib_server_protos = static_library( + 'server_protos', + server_protos_src + server_protos_headers, + dependencies: [wayland_client] +) # for the include directory + +server_protos = declare_dependency( + link_with: lib_server_protos, + sources: server_protos_headers, +) diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 000000000..b62d1bb44 --- /dev/null +++ b/protocols/wlr-input-inhibitor-unstable-v1.xml @@ -0,0 +1,67 @@ + + + + Copyright © 2018 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to prevent input events from being sent to + any surfaces but its own, which is useful for example in lock screen + software. It is assumed that access to this interface will be locked down + to whitelisted clients by the compositor. + + + + + Activates the input inhibitor. As long as the inhibitor is active, the + compositor will not send input events to other clients. + + + + + + + + + + + + While this resource exists, input to clients other than the owner of the + inhibitor resource will not receive input events. The client that owns + this resource will receive all input events normally. The compositor will + also disable all of its own input processing (such as keyboard shortcuts) + while the inhibitor is active. + + The compositor may continue to send input events to selected clients, + such as an on-screen keyboard (via the input-method protocol). + + + + + Destroy the inhibitor and allow other clients to receive input. + + + + diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml index e9f27e4fd..2bb72eddc 100644 --- a/protocols/wlr-layer-shell-unstable-v1.xml +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -1,5 +1,5 @@ - + Copyright © 2017 Drew DeVault @@ -25,7 +25,7 @@ THIS SOFTWARE. - + Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and @@ -47,12 +47,6 @@ or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. - After creating a layer_surface object and setting it up, the client - must perform an initial commit without any buffer attached. - The compositor will reply with a layer_surface.configure event. - The client must acknowledge it and is then allowed to attach a buffer - to map the surface. - You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. @@ -88,35 +82,17 @@ - - - - - - This request indicates that the client will not use the layer_shell - object any more. Objects that have been created through this instance - are not affected. - - - + An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like environment. - Layer surface state (layer, size, anchor, exclusive zone, - margin, interactivity) is double-buffered, and will be applied at the - time wl_surface.commit of the corresponding wl_surface is called. - - Attaching a null buffer to a layer surface unmaps it. - - Unmapping a layer_surface means that the surface cannot be shown by the - compositor until it is explicitly mapped again. The layer_surface - returns to the state it had right after layer_shell.get_layer_surface. - The client can re-map the surface by performing a commit without any - buffer attached, waiting for a configure event and handling it as usual. + Layer surface state (size, anchor, exclusive zone, margin, interactivity) + is double-buffered, and will be applied at the time wl_surface.commit of + the corresponding wl_surface is called. @@ -139,7 +115,7 @@ Requests that the compositor anchor the surface to the specified edges - and corners. If two orthogonal edges are specified (e.g. 'top' and + and corners. If two orthoginal edges are specified (e.g. 'top' and 'left'), then the anchor point will be the intersection of the edges (e.g. the top left corner of the output); otherwise the anchor point will be centered on that edge, or in the center if none is specified. @@ -151,25 +127,20 @@ - Requests that the compositor avoids occluding an area with other - surfaces. The compositor's use of this information is + Requests that the compositor avoids occluding an area of the surface + with other surfaces. The compositor's use of this information is implementation-dependent - do not assume that this region will not actually be occluded. - A positive value is only meaningful if the surface is anchored to one - edge or an edge and both perpendicular edges. If the surface is not - anchored, anchored to only two perpendicular edges (a corner), anchored - to only two parallel edges or anchored to all edges, a positive value - will be treated the same as zero. - - A positive zone is the distance from the edge in surface-local - coordinates to consider exclusive. + A positive value is only meaningful if the surface is anchored to an + edge, rather than a corner. The zone is the number of surface-local + coordinates from the edge that are considered exclusive. Surfaces that do not wish to have an exclusive zone may instead specify how they should interact with surfaces that do. If set to zero, the surface indicates that it would like to be moved to avoid occluding - surfaces with a positive exclusive zone. If set to -1, the surface - indicates that it would not like to be moved to accommodate for other + surfaces with a positive excluzive zone. If set to -1, the surface + indicates that it would not like to be moved to accomodate for other surfaces, and the compositor should extend it all the way to the edges it is anchored to. @@ -203,85 +174,21 @@ - - - Types of keyboard interaction possible for layer shell surfaces. The - rationale for this is twofold: (1) some applications are not interested - in keyboard events and not allowing them to be focused can improve the - desktop experience; (2) some applications will want to take exclusive - keyboard focus. - - - - - This value indicates that this surface is not interested in keyboard - events and the compositor should never assign it the keyboard focus. - - This is the default value, set for newly created layer shell surfaces. - - This is useful for e.g. desktop widgets that display information or - only have interaction with non-keyboard input devices. - - - - - Request exclusive keyboard focus if this surface is above the shell surface layer. - - For the top and overlay layers, the seat will always give - exclusive keyboard focus to the top-most layer which has keyboard - interactivity set to exclusive. If this layer contains multiple - surfaces with keyboard interactivity set to exclusive, the compositor - determines the one receiving keyboard events in an implementation- - defined manner. In this case, no guarantee is made when this surface - will receive keyboard focus (if ever). - - For the bottom and background layers, the compositor is allowed to use - normal focus semantics. - - This setting is mainly intended for applications that need to ensure - they receive all keyboard events, such as a lock screen or a password - prompt. - - - - - This requests the compositor to allow this surface to be focused and - unfocused by the user in an implementation-defined manner. The user - should be able to unfocus this surface even regardless of the layer - it is on. - - Typically, the compositor will want to use its normal mechanism to - manage keyboard focus between layer shell surfaces with this setting - and regular toplevels on the desktop layer (e.g. click to focus). - Nevertheless, it is possible for a compositor to require a special - interaction to focus or unfocus layer shell surfaces (e.g. requiring - a click even if focus follows the mouse normally, or providing a - keybinding to switch focus between layers). - - This setting is mainly intended for desktop shell components (e.g. - panels) that allow keyboard interaction. Using this option can allow - implementing a desktop shell that can be fully usable without the - mouse. - - - - - Set how keyboard events are delivered to this surface. By default, - layer shell surfaces do not receive keyboard events; this request can - be used to change this. - - This setting is inherited by child surfaces set by the get_popup - request. + Set to 1 to request that the seat send keyboard events to this layer + surface. For layers below the shell surface layer, the seat will use + normal focus semantics. For layers above the shell surface layers, the + seat will always give exclusive keyboard focus to the top-most layer + which has keyboard interactivity set to true. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. - Keyboard interactivity is double-buffered, see wl_surface.commit. + Events is double-buffered, see wl_surface.commit. - + @@ -366,8 +273,6 @@ - - @@ -376,32 +281,5 @@ - - - - - - Change the layer that the surface is rendered on. - - Layer is double-buffered, see wl_surface.commit. - - - - - - - - - Requests an edge for the exclusive zone to apply. The exclusive - edge will be automatically deduced from anchor points when possible, - but when the surface is anchored to a corner, it will be necessary - to set it explicitly to disambiguate, as it is not possible to deduce - which one of the two corner edges should be used. - - The edge must be one the surface is anchored to, otherwise the - invalid_exclusive_edge protocol error will be raised. - - - diff --git a/protocols/wlr-output-power-management-unstable-v1.xml b/protocols/wlr-output-power-management-unstable-v1.xml deleted file mode 100644 index 20dbb7760..000000000 --- a/protocols/wlr-output-power-management-unstable-v1.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - Copyright © 2019 Purism SPC - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - This protocol allows clients to control power management modes - of outputs that are currently part of the compositor space. The - intent is to allow special clients like desktop shells to power - down outputs when the system is idle. - - To modify outputs not currently part of the compositor space see - wlr-output-management. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - - - - This interface is a manager that allows creating per-output power - management mode controls. - - - - - Create an output power management mode control that can be used to - adjust the power management mode for a given output. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This object offers requests to set the power management mode of - an output. - - - - - - - - - - - - - - Set an output's power save mode to the given mode. The mode change - is effective immediately. If the output does not support the given - mode a failed event is sent. - - - - - - - Report the power management mode change of an output. - - The mode event is sent after an output changed its power - management mode. The reason can be a client using set_mode or the - compositor deciding to change an output's mode. - This event is also sent immediately when the object is created - so the client is informed about the current power management mode. - - - - - - - This event indicates that the output power management mode control - is no longer valid. This can happen for a number of reasons, - including: - - The output doesn't support power management - - Another client already has exclusive power management mode control - for this output - - The output disappeared - - Upon receiving this event, the client should destroy this object. - - - - - - Destroys the output power management mode control object. - - - - diff --git a/release.sh b/release.sh deleted file mode 100755 index 6ad8f2c5e..000000000 --- a/release.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh -eu - -prev=$(git describe --tags --abbrev=0) -next=$(meson rewrite kwargs info project / | jq -r '.kwargs["project#/"].version') - -case "$next" in -*-dev) - echo "This is a development version" - exit 1 - ;; -esac - -if [ "$prev" = "$next" ]; then - echo "Version not bumped in meson.build" - exit 1 -fi - -if ! git diff-index --quiet HEAD -- meson.build; then - echo "meson.build not committed" - exit 1 -fi - -shortlog="$(git shortlog --no-merges "$prev..")" -(echo "sway $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F - - -prefix=sway-$next -archive=$prefix.tar.gz -git archive --prefix="$prefix/" -o "$archive" "$next" -gpg --output "$archive".sig --detach-sig "$archive" - -git push --follow-tags -gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig" diff --git a/security.d/00-defaults.in b/security.d/00-defaults.in new file mode 100644 index 000000000..be7b9d063 --- /dev/null +++ b/security.d/00-defaults.in @@ -0,0 +1,50 @@ +# sway security rules +# +# Read sway-security(7) for details on how to secure your sway install. +# +# You MUST read this man page if you intend to attempt to secure your sway +# installation. +# +# DO NOT CHANGE THIS FILE. Override these defaults by writing new files in +# @sysconfdir@/sway/security.d/* + +# Configures enabled compositor features for specific programs +permit * fullscreen keyboard mouse +permit @prefix@/bin/swaylock lock +permit @prefix@/bin/swaybg background +permit @prefix@/bin/swaybar panel + +# Configures enabled IPC features for specific programs +ipc @prefix@/bin/swaymsg { + * enabled + + events { + * disabled + } +} + +ipc @prefix@/bin/swaybar { + bar-config enabled + outputs enabled + workspaces enabled + command enabled + + events { + workspace enabled + mode enabled + } +} + +ipc @prefix@/bin/swaylock { + outputs enabled +} + +# Limits the contexts from which certain commands are permitted +commands { + * all + + fullscreen binding criteria + bindsym config + exit binding + kill binding +} diff --git a/security.d/10-freebsd.in b/security.d/10-freebsd.in new file mode 100644 index 000000000..533b526a4 --- /dev/null +++ b/security.d/10-freebsd.in @@ -0,0 +1,19 @@ +# sway security rules +# +# FreeBSD does not support getting client PID from server side +# so we can not know the path to the client's binary. +# +# The solution for now is to be permissive and allow all +# features by default for any client. + +# Configures enabled compositor features for specific programs +permit * fullscreen keyboard mouse background screenshot panel lock + +ipc * { + * enabled + events { + * enabled + } +} + + diff --git a/sway.desktop b/sway.desktop index cc7fcee73..98c9af293 100644 --- a/sway.desktop +++ b/sway.desktop @@ -1,6 +1,5 @@ [Desktop Entry] Name=Sway -Comment=An i3-compatible Wayland compositor +Comment=SirCmpwn's Wayland window manager Exec=sway Type=Application -DesktopNames=sway;wlroots diff --git a/sway/commands.c b/sway/commands.c index c2c12ee65..3fc4f86e4 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809 #include #include #include @@ -8,6 +9,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/criteria.h" +#include "sway/security.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/tree/view.h" @@ -41,17 +43,14 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type } /* Keep alphabetized */ -static const struct cmd_handler handlers[] = { +static struct cmd_handler handlers[] = { { "assign", cmd_assign }, { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, - { "bindgesture", cmd_bindgesture }, - { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "client.background", cmd_client_noop }, { "client.focused", cmd_client_focused }, { "client.focused_inactive", cmd_client_focused_inactive }, - { "client.focused_tab_title", cmd_client_focused_tab_title }, { "client.placeholder", cmd_client_noop }, { "client.unfocused", cmd_client_unfocused }, { "client.urgent", cmd_client_urgent }, @@ -73,11 +72,12 @@ static const struct cmd_handler handlers[] = { { "fullscreen", cmd_fullscreen }, { "gaps", cmd_gaps }, { "hide_edge_borders", cmd_hide_edge_borders }, + { "include", cmd_include }, { "input", cmd_input }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, - { "new_float", cmd_new_float }, - { "new_window", cmd_new_window }, + { "new_float", cmd_default_floating_border }, + { "new_window", cmd_default_border }, { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "popup_during_fullscreen", cmd_popup_during_fullscreen }, @@ -91,19 +91,13 @@ static const struct cmd_handler handlers[] = { { "title_align", cmd_title_align }, { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, - { "unbindcode", cmd_unbindcode }, - { "unbindgesture", cmd_unbindgesture }, - { "unbindswitch", cmd_unbindswitch }, - { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, }; /* Config-time only commands. Keep alphabetized */ -static const struct cmd_handler config_handlers[] = { +static struct cmd_handler config_handlers[] = { { "default_orientation", cmd_default_orientation }, - { "include", cmd_include }, - { "primary_selection", cmd_primary_selection }, { "swaybg_command", cmd_swaybg_command }, { "swaynag_command", cmd_swaynag_command }, { "workspace_layout", cmd_workspace_layout }, @@ -111,18 +105,15 @@ static const struct cmd_handler config_handlers[] = { }; /* Runtime-only commands. Keep alphabetized */ -static const struct cmd_handler command_handlers[] = { - { "allow_tearing", cmd_allow_tearing }, +static struct cmd_handler command_handlers[] = { { "border", cmd_border }, { "create_output", cmd_create_output }, { "exit", cmd_exit }, { "floating", cmd_floating }, { "fullscreen", cmd_fullscreen }, - { "inhibit_idle", cmd_inhibit_idle }, { "kill", cmd_kill }, { "layout", cmd_layout }, { "mark", cmd_mark }, - { "max_render_time", cmd_max_render_time }, { "move", cmd_move }, { "nop", cmd_nop }, { "opacity", cmd_opacity }, @@ -130,7 +121,6 @@ static const struct cmd_handler command_handlers[] = { { "rename", cmd_rename }, { "resize", cmd_resize }, { "scratchpad", cmd_scratchpad }, - { "shortcuts_inhibitor", cmd_shortcuts_inhibitor }, { "split", cmd_split }, { "splith", cmd_splith }, { "splitt", cmd_splitt }, @@ -148,41 +138,50 @@ static int handler_compare(const void *_a, const void *_b) { return strcasecmp(a->command, b->command); } -const struct cmd_handler *find_handler(const char *line, - const struct cmd_handler *handlers, size_t handlers_size) { - if (!handlers || !handlers_size) { - return NULL; +struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, + int handlers_size) { + struct cmd_handler d = { .command=line }; + struct cmd_handler *res = NULL; + sway_log(SWAY_DEBUG, "find_handler(%s)", line); + + bool config_loading = config->reading || !config->active; + + if (!config_loading) { + res = bsearch(&d, command_handlers, + sizeof(command_handlers) / sizeof(struct cmd_handler), + sizeof(struct cmd_handler), handler_compare); + + if (res) { + return res; + } } - const struct cmd_handler query = { .command = line }; - return bsearch(&query, handlers, + + if (config->reading) { + res = bsearch(&d, config_handlers, + sizeof(config_handlers) / sizeof(struct cmd_handler), + sizeof(struct cmd_handler), handler_compare); + + if (res) { + return res; + } + } + + if (!cmd_handlers) { + cmd_handlers = handlers; + handlers_size = sizeof(handlers); + } + + res = bsearch(&d, cmd_handlers, handlers_size / sizeof(struct cmd_handler), sizeof(struct cmd_handler), handler_compare); + + return res; } -static const struct cmd_handler *find_handler_ex(char *line, - const struct cmd_handler *config_handlers, size_t config_handlers_size, - const struct cmd_handler *command_handlers, size_t command_handlers_size, - const struct cmd_handler *handlers, size_t handlers_size) { - const struct cmd_handler *handler = NULL; - if (config->reading) { - handler = find_handler(line, config_handlers, config_handlers_size); - } else if (config->active) { - handler = find_handler(line, command_handlers, command_handlers_size); - } - return handler ? handler : find_handler(line, handlers, handlers_size); -} - -static const struct cmd_handler *find_core_handler(char *line) { - return find_handler_ex(line, config_handlers, sizeof(config_handlers), - command_handlers, sizeof(command_handlers), - handlers, sizeof(handlers)); -} - -static void set_config_node(struct sway_node *node, bool node_overridden) { +static void set_config_node(struct sway_node *node) { config->handler_context.node = node; config->handler_context.container = NULL; config->handler_context.workspace = NULL; - config->handler_context.node_overridden = node_overridden; if (node == NULL) { return; @@ -191,7 +190,7 @@ static void set_config_node(struct sway_node *node, bool node_overridden) { switch (node->type) { case N_CONTAINER: config->handler_context.container = node->sway_container; - config->handler_context.workspace = node->sway_container->pending.workspace; + config->handler_context.workspace = node->sway_container->workspace; break; case N_WORKSPACE: config->handler_context.workspace = node->sway_workspace; @@ -204,10 +203,12 @@ static void set_config_node(struct sway_node *node, bool node_overridden) { list_t *execute_command(char *_exec, struct sway_seat *seat, struct sway_container *con) { + list_t *res_list = create_list(); + char *exec = strdup(_exec); + char *head = exec; + char *cmdlist; char *cmd; - char matched_delim = ';'; - list_t *containers = NULL; - bool using_criteria = false; + list_t *views = NULL; if (seat == NULL) { // passing a NULL seat means we just pick the default seat @@ -217,119 +218,94 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, } } - char *exec = strdup(_exec); - char *head = exec; - list_t *res_list = create_list(); - - if (!res_list || !exec) { - return NULL; - } - config->handler_context.seat = seat; + head = exec; do { - for (; isspace(*head); ++head) {} // Extract criteria (valid for this command list only). - if (matched_delim == ';') { - using_criteria = false; - if (*head == '[') { - char *error = NULL; - struct criteria *criteria = criteria_parse(head, &error); - if (!criteria) { - list_add(res_list, - cmd_results_new(CMD_INVALID, "%s", error)); - free(error); - goto cleanup; - } - list_free(containers); - containers = criteria_get_containers(criteria); - head += strlen(criteria->raw); - criteria_destroy(criteria); - using_criteria = true; - // Skip leading whitespace - for (; isspace(*head); ++head) {} + config->handler_context.using_criteria = false; + if (*head == '[') { + char *error = NULL; + struct criteria *criteria = criteria_parse(head, &error); + if (!criteria) { + list_add(res_list, cmd_results_new(CMD_INVALID, "%s", error)); + free(error); + goto cleanup; } + views = criteria_get_views(criteria); + head += strlen(criteria->raw); + criteria_destroy(criteria); + config->handler_context.using_criteria = true; + // Skip leading whitespace + for (; isspace(*head); ++head) {} } // Split command list - cmd = argsep(&head, ";,", &matched_delim); - for (; isspace(*cmd); ++cmd) {} - - if (strcmp(cmd, "") == 0) { - sway_log(SWAY_INFO, "Ignoring empty command."); - continue; - } - sway_log(SWAY_INFO, "Handling command '%s'", cmd); - //TODO better handling of argv - int argc; - char **argv = split_args(cmd, &argc); - if (strcmp(argv[0], "exec") != 0 && - strcmp(argv[0], "exec_always") != 0 && - strcmp(argv[0], "mode") != 0) { - for (int i = 1; i < argc; ++i) { - if (*argv[i] == '\"' || *argv[i] == '\'') { - strip_quotes(argv[i]); + cmdlist = argsep(&head, ";"); + for (; isspace(*cmdlist); ++cmdlist) {} + do { + // Split commands + cmd = argsep(&cmdlist, ","); + for (; isspace(*cmd); ++cmd) {} + if (strcmp(cmd, "") == 0) { + sway_log(SWAY_INFO, "Ignoring empty command."); + continue; + } + sway_log(SWAY_INFO, "Handling command '%s'", cmd); + //TODO better handling of argv + int argc; + char **argv = split_args(cmd, &argc); + if (strcmp(argv[0], "exec") != 0 && + strcmp(argv[0], "exec_always") != 0) { + int i; + for (i = 1; i < argc; ++i) { + if (*argv[i] == '\"' || *argv[i] == '\'') { + strip_quotes(argv[i]); + } } } - } - const struct cmd_handler *handler = find_core_handler(argv[0]); - if (!handler) { - list_add(res_list, cmd_results_new(CMD_INVALID, - "Unknown/invalid command '%s'", argv[0])); - free_argv(argc, argv); - goto cleanup; - } - - // 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]); - } - - - if (!using_criteria) { - if (con) { - set_config_node(&con->node, true); - } else { - set_config_node(seat_get_focus_inactive(seat, &root->node), - false); - } - struct cmd_results *res = handler->handle(argc-1, argv+1); - list_add(res_list, res); - if (res->status == CMD_INVALID) { + struct cmd_handler *handler = find_handler(argv[0], NULL, 0); + if (!handler) { + list_add(res_list, cmd_results_new(CMD_INVALID, + "Unknown/invalid command '%s'", argv[0])); free_argv(argc, argv); goto cleanup; } - } else if (containers->length == 0) { - list_add(res_list, - cmd_results_new(CMD_FAILURE, "No matching node.")); - } else { - struct cmd_results *fail_res = NULL; - for (int i = 0; i < containers->length; ++i) { - struct sway_container *container = containers->items[i]; - set_config_node(&container->node, true); + + // 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) { + // The container or workspace which this command will run on. + struct sway_node *node = con ? &con->node : + seat_get_focus_inactive(seat, &root->node); + set_config_node(node); struct cmd_results *res = handler->handle(argc-1, argv+1); - if (res->status == CMD_SUCCESS) { - free_cmd_results(res); - } else { - // last failure will take precedence - if (fail_res) { - free_cmd_results(fail_res); - } - fail_res = res; + list_add(res_list, res); + if (res->status == CMD_INVALID) { + free_argv(argc, argv); + goto cleanup; + } + } else { + for (int i = 0; i < views->length; ++i) { + struct sway_view *view = views->items[i]; + set_config_node(&view->container->node); + struct cmd_results *res = handler->handle(argc-1, argv+1); + list_add(res_list, res); if (res->status == CMD_INVALID) { - list_add(res_list, fail_res); free_argv(argc, argv); goto cleanup; } } } - list_add(res_list, - fail_res ? fail_res : cmd_results_new(CMD_SUCCESS, NULL)); - } - free_argv(argc, argv); + free_argv(argc, argv); + } while(cmdlist); } while(head); cleanup: free(exec); - list_free(containers); + list_free(views); return res_list; } @@ -379,15 +355,12 @@ struct cmd_results *config_command(char *exec, char **new_block) { // Determine the command handler sway_log(SWAY_INFO, "Config command: %s", exec); - const struct cmd_handler *handler = find_core_handler(argv[0]); + struct cmd_handler *handler = find_handler(argv[0], NULL, 0); if (!handler || !handler->handle) { - if (handler) { - results = cmd_results_new(CMD_INVALID, - "Command '%s' is shimmed, but unimplemented", argv[0]); - } else { - results = cmd_results_new(CMD_INVALID, - "Unknown/invalid command '%s'", argv[0]); - } + const char *error = handler + ? "Command '%s' is shimmed, but unimplemented" + : "Unknown/invalid command '%s'"; + results = cmd_results_new(CMD_INVALID, error, argv[0]); goto cleanup; } @@ -412,10 +385,7 @@ 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_bindgesture && handler->handle != cmd_set - && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { strip_quotes(argv[i]); } @@ -431,12 +401,12 @@ cleanup: } struct cmd_results *config_subcommand(char **argv, int argc, - const struct cmd_handler *handlers, size_t handlers_size) { + struct cmd_handler *handlers, size_t handlers_size) { char *command = join_args(argv, argc); sway_log(SWAY_DEBUG, "Subcommand: %s", command); free(command); - const struct cmd_handler *handler = find_handler(argv[0], handlers, + struct cmd_handler *handler = find_handler(argv[0], handlers, handlers_size); if (!handler) { return cmd_results_new(CMD_INVALID, @@ -466,13 +436,63 @@ struct cmd_results *config_commands_command(char *exec) { goto cleanup; } - const struct cmd_handler *handler = find_handler(cmd, NULL, 0); + struct cmd_handler *handler = find_handler(cmd, NULL, 0); if (!handler && strcmp(cmd, "*") != 0) { results = cmd_results_new(CMD_INVALID, "Unknown/invalid command '%s'", cmd); goto cleanup; } + enum command_context context = 0; + + struct { + char *name; + enum command_context context; + } context_names[] = { + { "config", CONTEXT_CONFIG }, + { "binding", CONTEXT_BINDING }, + { "ipc", CONTEXT_IPC }, + { "criteria", CONTEXT_CRITERIA }, + { "all", CONTEXT_ALL }, + }; + + for (int i = 1; i < argc; ++i) { + size_t j; + for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) { + if (strcmp(context_names[j].name, argv[i]) == 0) { + break; + } + } + if (j == sizeof(context_names) / sizeof(context_names[0])) { + results = cmd_results_new(CMD_INVALID, + "Invalid command context %s", argv[i]); + goto cleanup; + } + context |= context_names[j].context; + } + + struct command_policy *policy = NULL; + for (int i = 0; i < config->command_policies->length; ++i) { + struct command_policy *p = config->command_policies->items[i]; + if (strcmp(p->command, cmd) == 0) { + policy = p; + break; + } + } + if (!policy) { + policy = alloc_command_policy(cmd); + if (!sway_assert(policy, "Unable to allocate security policy")) { + results = cmd_results_new(CMD_INVALID, + "Unable to allocate memory"); + goto cleanup; + } + list_add(config->command_policies, policy); + } + policy->context = context; + + sway_log(SWAY_INFO, "Set command policy for %s to %d", + policy->command, policy->context); + results = cmd_results_new(CMD_SUCCESS, NULL); cleanup: @@ -489,10 +509,14 @@ struct cmd_results *cmd_results_new(enum cmd_status status, } results->status = status; if (format) { + char *error = malloc(256); va_list args; va_start(args, format); - results->error = vformat_str(format, args); + if (error) { + vsnprintf(error, 256, format, args); + } va_end(args); + results->error = error; } else { results->error = NULL; } @@ -526,3 +550,34 @@ char *cmd_results_to_json(list_t *res_list) { json_object_put(result_array); return res; } + +/** + * Check and add color to buffer. + * + * return error object, or NULL if color is valid. + */ +struct cmd_results *add_color(char *buffer, const char *color) { + int len = strlen(color); + if (len != 7 && len != 9) { + return cmd_results_new(CMD_INVALID, + "Invalid color definition %s", color); + } + if (color[0] != '#') { + return cmd_results_new(CMD_INVALID, + "Invalid color definition %s", color); + } + for (int i = 1; i < len; ++i) { + if (!isxdigit(color[i])) { + return cmd_results_new(CMD_INVALID, + "Invalid color definition %s", color); + } + } + strcpy(buffer, color); + // add default alpha channel if color was defined without it + if (len == 7) { + buffer[7] = 'f'; + buffer[8] = 'f'; + } + buffer[9] = '\0'; + return NULL; +} diff --git a/sway/commands/allow_tearing.c b/sway/commands/allow_tearing.c deleted file mode 100644 index ee5941381..000000000 --- a/sway/commands/allow_tearing.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include "sway/config.h" -#include "sway/tree/view.h" -#include "util.h" - -struct cmd_results *cmd_allow_tearing(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "allow_tearing", EXPECTED_AT_LEAST, 1))) { - return error; - } - - struct sway_container *container = config->handler_context.container; - if (!container || !container->view) { - return cmd_results_new(CMD_INVALID, "Tearing can only be allowed on views"); - } - - bool wants_tearing = parse_boolean(argv[0], true); - - struct sway_view *view = container->view; - view->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE : - TEARING_OVERRIDE_FALSE; - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 5bcbb1644..976bc3cc5 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" @@ -16,14 +17,14 @@ struct cmd_results *cmd_assign(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, "%s", err_str); + error = cmd_results_new(CMD_INVALID, err_str); free(err_str); return error; } --argc; ++argv; - if (has_prefix(*argv, "→")) { + if (strncmp(*argv, "→", strlen("→")) == 0) { if (argc < 2) { free(criteria); return cmd_results_new(CMD_INVALID, "Missing workspace"); diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 635e895be..82441f9e4 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -1,13 +1,13 @@ +#define _POSIX_C_SOURCE 200809 #include #include #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/ipc-server.h" #include "log.h" // Must be in alphabetical order for bsearch -static const struct cmd_handler bar_handlers[] = { +static struct cmd_handler bar_handlers[] = { { "bindcode", bar_cmd_bindcode }, { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, { "bindsym", bar_cmd_bindsym }, @@ -32,15 +32,12 @@ static const 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 }, - { "workspace_min_width", bar_cmd_workspace_min_width }, { "wrap_scroll", bar_cmd_wrap_scroll }, }; // Must be in alphabetical order for bsearch -static const struct cmd_handler bar_config_handlers[] = { +static struct cmd_handler bar_config_handlers[] = { { "id", bar_cmd_id }, { "swaybar_command", bar_cmd_swaybar_command }, }; @@ -57,79 +54,80 @@ struct cmd_results *cmd_bar(int argc, char **argv) { return error; } - char *id = NULL; + bool spawn = false; + struct bar_config *bar = NULL; if (strcmp(argv[0], "id") != 0 && is_subcommand(argv[1])) { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *item = config->bars->items[i]; if (strcmp(item->id, argv[0]) == 0) { sway_log(SWAY_DEBUG, "Selecting bar: %s", argv[0]); - config->current_bar = item; + bar = item; break; } } - if (!config->current_bar) { - id = strdup(argv[0]); + if (!bar) { + spawn = !config->reading; + sway_log(SWAY_DEBUG, "Creating bar: %s", argv[0]); + bar = default_bar_config(); + if (!bar) { + return cmd_results_new(CMD_FAILURE, + "Unable to allocate bar state"); + } + + bar->id = strdup(argv[0]); } + config->current_bar = bar; ++argv; --argc; - } else if (config->reading && !config->current_bar) { - id = format_str("bar-%d", config->bars->length); - if (!id) { - return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); + } + + 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"); } - } 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."); + + // 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_INVALID, - "Unknown/invalid command '%s'", argv[1]); + 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); } - if (id) { - sway_log(SWAY_DEBUG, "Creating bar: %s", id); - config->current_bar = default_bar_config(); - if (!config->current_bar) { - free(id); - return cmd_results_new(CMD_FAILURE, "Unable to allocate bar config"); - } - config->current_bar->id = id; - } - - struct cmd_results *res = NULL; if (find_handler(argv[0], bar_config_handlers, sizeof(bar_config_handlers))) { if (config->reading) { - res = config_subcommand(argv, argc, bar_config_handlers, + return config_subcommand(argv, argc, bar_config_handlers, sizeof(bar_config_handlers)); - } else { - res = cmd_results_new(CMD_INVALID, - "Can only be used in the config file"); + } else if (spawn) { + for (int i = config->bars->length - 1; i >= 0; i--) { + struct bar_config *bar = config->bars->items[i]; + if (bar == config->current_bar) { + list_del(config->bars, i); + free_bar_config(bar); + break; + } + } } - } else { - res = config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); + return cmd_results_new(CMD_INVALID, + "Can only be used in the config file."); } - if (res && res->status != CMD_SUCCESS) { - if (id) { - free_bar_config(config->current_bar); - config->current_bar = NULL; - id = NULL; - } - return res; - } - - if (id) { - list_add(config->bars, config->current_bar); - } - - if (!config->reading && config->current_bar) { - ipc_event_barconfig_update(config->current_bar); - if (id) { + struct cmd_results *res = + config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); + if (!config->reading) { + if (spawn) { load_swaybar(config->current_bar); } config->current_bar = NULL; } - return res; } diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index 8a837e3f1..17030a05f 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c @@ -9,72 +9,15 @@ #include "log.h" #include "stringop.h" -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 (!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"; - } - +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, minargs))) { + if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, 2))) { return error; } + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "No bar defined."); + } struct bar_binding *binding = calloc(1, sizeof(struct bar_binding)); if (!binding) { @@ -96,34 +39,46 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, } if (message) { free_bar_binding(binding); - error = cmd_results_new(CMD_INVALID, "%s", message); + error = cmd_results_new(CMD_INVALID, message); free(message); return error; } else if (!binding->button) { free_bar_binding(binding); return cmd_results_new(CMD_INVALID, "Unknown button %s", argv[0]); } - list_t *bindings = config->current_bar->bindings; - if (unbind) { - return binding_remove(binding, bindings); - } + const char *name = get_mouse_button_name(binding->button); binding->command = join_args(argv + 1, argc - 1); - return binding_add(binding, bindings); + + 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); } struct cmd_results *bar_cmd_bindcode(int argc, char **argv) { - return bar_cmd_bind(argc, argv, true, false); + return bar_cmd_bind(argc, argv, true); } struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { - 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); + return bar_cmd_bind(argc, argv, false); } diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index b58d8a838..29c93ddc6 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c @@ -10,7 +10,10 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { return error; } - config->current_bar->binding_mode_indicator = + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "No bar defined."); + } + 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/colors.c b/sway/commands/bar/colors.c index da606c1b8..7921db0de 100644 --- a/sway/commands/bar/colors.c +++ b/sway/commands/bar/colors.c @@ -1,10 +1,8 @@ #include #include "sway/commands.h" -#include "log.h" -#include "util.h" // Must be in alphabetical order for bsearch -static const struct cmd_handler bar_colors_handlers[] = { +static struct cmd_handler bar_colors_handlers[] = { { "active_workspace", bar_colors_cmd_active_workspace }, { "background", bar_colors_cmd_background }, { "binding_mode", bar_colors_cmd_binding_mode }, @@ -18,64 +16,42 @@ static const struct cmd_handler bar_colors_handlers[] = { { "urgent_workspace", bar_colors_cmd_urgent_workspace }, }; -static char *hex_to_rgba_hex(const char *hex) { - uint32_t color; - if (!parse_color(hex, &color)) { - return NULL; - } - char *rgba = malloc(10); - if (!rgba) { - return NULL; - } - snprintf(rgba, 10, "#%08x", color); - return rgba; -} - static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { return error; } - - char *rgba = hex_to_rgba_hex(argv[0]); - if (!rgba) { - return cmd_results_new(CMD_INVALID, "Invalid color: %s", argv[0]); + if (!*color && !(*color = malloc(10))) { + return NULL; + } + error = add_color(*color, argv[0]); + if (error) { + return error; } - - free(*color); - *color = rgba; return cmd_results_new(CMD_SUCCESS, NULL); } static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 3))) { - return error; + if (argc != 3) { + return cmd_results_new(CMD_INVALID, + "Command '%s' requires exactly three color values", cmd_name); } - - char *rgba[3] = {0}; - for (int i = 0; i < 3; i++) { - rgba[i] = hex_to_rgba_hex(argv[i]); - if (!rgba[i]) { - return cmd_results_new(CMD_INVALID, "Invalid color: %s", argv[i]); + for (size_t i = 0; i < 3; i++) { + if (!*colors[i] && !(*(colors[i]) = malloc(10))) { + return NULL; + } + error = add_color(*(colors[i]), argv[i]); + if (error) { + return error; } } - - for (int i = 0; i < 3; i++) { - free(*colors[i]); - *colors[i] = rgba[i]; - } - return cmd_results_new(CMD_SUCCESS, NULL); } struct cmd_results *bar_cmd_colors(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "colors", EXPECTED_AT_LEAST, 1))) { - return error; - } return config_subcommand(argv, argc, bar_colors_handlers, sizeof(bar_colors_handlers)); } diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 51ca20ed5..cf1f759ec 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "log.h" @@ -8,22 +9,12 @@ 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); - - if (has_prefix(font, "pango:")) { - if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { - config->current_bar->pango_markup = true; - } - config->current_bar->font = strdup(font + 6); - } else { - if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { - config->current_bar->pango_markup = false; - } - config->current_bar->font = strdup(font); - } - - free(font); + config->current_bar->font = font; sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id); return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/bar/gaps.c b/sway/commands/bar/gaps.c index 9fa3e756a..83480fb50 100644 --- a/sway/commands/bar/gaps.c +++ b/sway/commands/bar/gaps.c @@ -13,6 +13,9 @@ 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; @@ -21,7 +24,7 @@ struct cmd_results *bar_cmd_gaps(int argc, char **argv) { int amount = strtol(argv[i], &end, 10); if (strlen(end) && strcasecmp(end, "px") != 0) { return cmd_results_new(CMD_INVALID, - "Expected 'bar [] gaps | " + "Expected 'bar [] gaps | " " | '"); } diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 7b38831e4..b2c2d245d 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" @@ -22,7 +23,7 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, return cmd_results_new(CMD_INVALID, "Invalid value %s", hidden_state); } if (strcmp(old_state, bar->hidden_state) != 0) { - if (!config->current_bar) { + if (!config->reading) { ipc_event_barconfig_update(bar); } sway_log(SWAY_DEBUG, "Setting hidden_state: '%s' for bar: %s", @@ -46,14 +47,8 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { "Unexpected value %s in config mode", argv[1]); } - if (config->current_bar && argc == 2 && - strcmp(config->current_bar->id, argv[1]) != 0) { - return cmd_results_new(CMD_INVALID, "Conflicting bar ids: %s and %s", - config->current_bar->id, argv[1]); - } - const char *state = argv[0]; - if (config->current_bar) { + if (config->reading) { error = bar_set_hidden_state(config->current_bar, state); } else { const char *id = argc == 2 ? argv[1] : NULL; diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index fee217099..54b7b16e3 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "config.h" #include "sway/commands.h" @@ -11,6 +12,10 @@ 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/id.c b/sway/commands/bar/id.c index 46cf4ca9a..a9a617430 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index d69e910bf..68a80abf6 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" @@ -19,14 +20,12 @@ 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); } if (strcmp(old_mode, bar->mode) != 0) { - if (!config->current_bar) { + if (!config->reading) { ipc_event_barconfig_update(bar); } sway_log(SWAY_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); @@ -50,14 +49,8 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { "Unexpected value %s in config mode", argv[1]); } - if (config->current_bar && argc == 2 && - strcmp(config->current_bar->id, argv[1]) != 0) { - return cmd_results_new(CMD_INVALID, "Conflicting bar ids: %s and %s", - config->current_bar->id, argv[1]); - } - const char *mode = argv[0]; - if (config->current_bar) { + if (config->reading) { error = bar_set_mode(config->current_bar, mode); } else { const char *id = argc == 2 ? argv[1] : NULL; diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index 983d21795..d25d01d4b 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c @@ -10,6 +10,10 @@ 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 51730176d..956c19597 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" @@ -9,6 +10,9 @@ 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; @@ -20,19 +24,16 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { bool add_output = true; if (strcmp("*", output) == 0) { // remove all previous defined outputs and replace with '*' - while (outputs->length) { - free(outputs->items[0]); - list_del(outputs, 0); + for (int i = 0; i < outputs->length; ++i) { + free(outputs->items[i]); + list_del(outputs, i); } } else { - // only add output if not already defined, if the list has '*', remove - // it, in favor of a manual list + // only add output if not already defined with either the same + // name or as '*' for (int i = 0; i < outputs->length; ++i) { const char *find = outputs->items[i]; - if (strcmp("*", find) == 0) { - free(outputs->items[i]); - list_del(outputs, i); - } else if (strcmp(output, find) == 0) { + if (strcmp("*", find) == 0 || strcmp(output, find) == 0) { add_output = false; break; } diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index ee51390da..b0958cf11 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c @@ -9,8 +9,11 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) { return error; } - config->current_bar->pango_markup = - parse_boolean(argv[0], config->current_bar->pango_markup); + 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); 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 94f530ecd..4456d7241 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" @@ -8,6 +9,9 @@ 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 50e9a873d..76e99b490 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "log.h" @@ -7,6 +8,9 @@ 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 bb92e8e07..0b58e5fa2 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c @@ -8,6 +8,9 @@ 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; @@ -19,5 +22,10 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) { } else { free(new_command); } + + if (config->active && !config->validating) { + load_swaybar(config->current_bar); + } + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/bar/strip_workspace_name.c b/sway/commands/bar/strip_workspace_name.c index 764321a89..1aa393599 100644 --- a/sway/commands/bar/strip_workspace_name.c +++ b/sway/commands/bar/strip_workspace_name.c @@ -10,6 +10,9 @@ 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 2d7fe1a7f..56c4c4a12 100644 --- a/sway/commands/bar/strip_workspace_numbers.c +++ b/sway/commands/bar/strip_workspace_numbers.c @@ -10,7 +10,10 @@ 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 0892a898a..b54bafa9f 100644 --- a/sway/commands/bar/swaybar_command.c +++ b/sway/commands/bar/swaybar_command.c @@ -8,6 +8,9 @@ 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 3dc9bc4cc..7fe67c423 100644 --- a/sway/commands/bar/tray_bind.c +++ b/sway/commands/bar/tray_bind.c @@ -12,6 +12,9 @@ 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) { @@ -26,7 +29,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { } if (message) { free(binding); - error = cmd_results_new(CMD_INVALID, "%s", message); + error = cmd_results_new(CMD_INVALID, message); free(message); return error; } else if (!binding->button) { @@ -52,7 +55,6 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { } } if (!binding->command) { - free(binding); return cmd_results_new(CMD_INVALID, "[Bar %s] Invalid tray command %s", config->current_bar->id, argv[1]); } diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index 679facf7a..16e16f603 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "config.h" #include "sway/commands.h" @@ -12,6 +13,10 @@ 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(); @@ -23,21 +28,9 @@ struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { free(outputs->items[i]); } outputs->length = 0; - } else if (strcmp(argv[0], "*") == 0) { - sway_log(SWAY_DEBUG, "Showing tray on all outputs for bar: %s", - config->current_bar->id); - while (outputs->length) { - free(outputs->items[0]); - list_del(outputs, 0); - } - return cmd_results_new(CMD_SUCCESS, NULL); } else { sway_log(SWAY_DEBUG, "Showing tray on output '%s' for bar: %s", argv[0], config->current_bar->id); - if (outputs->length == 1 && strcmp(outputs->items[0], "none") == 0) { - free(outputs->items[0]); - list_del(outputs, 0); - } } list_add(outputs, strdup(argv[0])); diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c index f90b6003f..f43cfe4fe 100644 --- a/sway/commands/bar/tray_padding.c +++ b/sway/commands/bar/tray_padding.c @@ -15,6 +15,9 @@ 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 6bfb16165..792ef605a 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c @@ -9,7 +9,10 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) { return error; } - config->current_bar->workspace_buttons = + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "No bar defined."); + } + 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/workspace_min_width.c b/sway/commands/bar/workspace_min_width.c deleted file mode 100644 index 8d65592ca..000000000 --- a/sway/commands/bar/workspace_min_width.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include "config.h" -#include "sway/commands.h" -#include "sway/config.h" -#include "log.h" - -struct cmd_results *bar_cmd_workspace_min_width(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "workspace_min_width", EXPECTED_AT_LEAST, 1))) { - return error; - } - - struct bar_config *bar = config->current_bar; - - char *end; - int min_width = strtol(argv[0], &end, 10); - if (min_width < 0 || (*end != '\0' && strcasecmp(end, "px") != 0)) { - return cmd_results_new(CMD_INVALID, - "[Bar %s] Invalid minimum workspace button width value: %s", - bar->id, argv[0]); - } - - if (argc == 2 && strcasecmp(argv[1], "px") != 0) { - return cmd_results_new(CMD_INVALID, - "Expected 'workspace_min_width [px]'"); - } - - sway_log(SWAY_DEBUG, "[Bar %s] Setting minimum workspace button width to %d", - bar->id, min_width); - config->current_bar->workspace_min_width = min_width; - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index f57e393df..decd238d4 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c @@ -9,7 +9,10 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) { return error; } - config->current_bar->wrap_scroll = + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "No bar defined."); + } + 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 627d994a0..172e6b8ae 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -7,7 +8,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" #include "sway/ipc-server.h" @@ -24,38 +24,11 @@ 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->trigger != binding_b->trigger) { - 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 @@ -72,17 +45,12 @@ static bool binding_key_compare(struct sway_binding *binding_a, } uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER - | BINDING_CONTENTS | BINDING_TITLEBAR | BINDING_LOCKED - | BINDING_INHIBITED; + | BINDING_CONTENTS | BINDING_TITLEBAR; if ((binding_a->flags & conflict_generating_flags) != (binding_b->flags & conflict_generating_flags)) { return false; } - if (binding_a->group != binding_b->group) { - return false; - } - if (binding_a->modifiers ^ binding_b->modifiers) { return false; } @@ -126,7 +94,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, if (!button) { if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, "%s", message); + cmd_results_new(CMD_INVALID, message); free(message); return error; } else { @@ -142,10 +110,10 @@ static struct cmd_results *identify_key(const char* name, bool first_key, if (!button) { if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, "%s", message); + cmd_results_new(CMD_INVALID, message); free(message); return error; - } else { + } else if (!button) { return cmd_results_new(CMD_INVALID, "Unknown button %s", name); } } @@ -181,7 +149,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, uint32_t button = get_mouse_bindsym(name, &message); if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, "%s", message); + cmd_results_new(CMD_INVALID, message); free(message); return error; } else if (button) { @@ -206,133 +174,12 @@ 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, bool unbind) { - const char *bindtype; - int minargs = 2; - if (unbind) { - bindtype = bindcode ? "unbindcode" : "unbindsym"; - minargs--; - } else { - bindtype = bindcode ? "bindcode": "bindsym"; - } + bool bindcode) { + const char *bindtype = bindcode ? "bindcode" : "bindsym"; struct cmd_results *error = NULL; - if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, 2))) { return error; } @@ -342,7 +189,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } binding->input = strdup("*"); binding->keys = create_list(); - binding->group = XKB_LAYOUT_INVALID; binding->modifiers = 0; binding->flags = 0; binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; @@ -350,31 +196,24 @@ 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; } else if (strcmp("--locked", argv[0]) == 0) { binding->flags |= BINDING_LOCKED; - } else if (strcmp("--inhibited", argv[0]) == 0) { - binding->flags |= BINDING_INHIBITED; } else if (strcmp("--whole-window", argv[0]) == 0) { 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 (has_prefix(argv[0], "--input-device=")) { + } else if (strncmp("--input-device=", argv[0], + strlen("--input-device=")) == 0) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); - strip_quotes(binding->input); } else if (strcmp("--no-warn", argv[0]) == 0) { warn = false; - } else if (strcmp("--no-repeat", argv[0]) == 0) { - binding->flags |= BINDING_NOREPEAT; } else { break; } @@ -387,44 +226,17 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, BINDING_MOUSECODE : BINDING_MOUSESYM; } - if (argc < minargs) { + if (argc < 2) { free_sway_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command " - "(expected at least %d non-option arguments, got %d)", - bindtype, minargs, argc); + "(expected at least 2 non-option arguments, got %d)", bindtype, 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 group - if (has_prefix(split->items[i], "Group")) { - if (binding->group != XKB_LAYOUT_INVALID) { - free_sway_binding(binding); - list_free_items_and_destroy(split); - return cmd_results_new(CMD_FAILURE, - "Only one group can be specified"); - } - char *end; - int group = strtol(split->items[i] + strlen("Group"), &end, 10); - if (group < 1 || group > 4 || end[0] != '\0') { - free_sway_binding(binding); - list_free_items_and_destroy(split); - return cmd_results_new(CMD_FAILURE, "Invalid group"); - } - binding->group = group - 1; - continue; - } else if (strcmp(split->items[i], "Mode_switch") == 0) { - // For full i3 compatibility, Mode_switch is an alias for Group2 - if (binding->group != XKB_LAYOUT_INVALID) { - free_sway_binding(binding); - list_free_items_and_destroy(split); - return cmd_results_new(CMD_FAILURE, - "Only one group can be specified"); - } - binding->group = 1; - } - // Check for a modifier key uint32_t mod; if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) { @@ -453,6 +265,7 @@ 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 @@ -466,12 +279,6 @@ 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; @@ -481,144 +288,49 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, mode_bindings = config->current_mode->mouse_bindings; } - 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 if (strcmp("--reload", argv[0]) == 0) { - binding->flags |= BINDING_RELOAD; - } else { - break; + // 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; } - argv++; - argc--; } - if (argc < minargs) { - free(binding); - return cmd_results_new(CMD_FAILURE, - "Invalid %s command (expected at least %d " - "non-option arguments, got %d)", bindtype, minargs, argc); + if (!overwritten) { + list_add(mode_bindings, binding); } - 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); - } - 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; -#if HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE - } else if (strcmp(split->items[0], "keypad_slide") == 0) { - binding->type = WLR_SWITCH_TYPE_KEYPAD_SLIDE; -#endif - } else { - free_switch_binding(binding); - return cmd_results_new(CMD_FAILURE, - "Invalid %s command (expected switch binding: " - "unknown switch %s)", bindtype, - (const char *)split->items[0]); - } - if (strcmp(split->items[1], "on") == 0) { - binding->trigger = SWAY_SWITCH_TRIGGER_ON; - } else if (strcmp(split->items[1], "off") == 0) { - binding->trigger = SWAY_SWITCH_TRIGGER_OFF; - } else if (strcmp(split->items[1], "toggle") == 0) { - binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE; - } else { - free_switch_binding(binding); - return cmd_results_new(CMD_FAILURE, - "Invalid %s command " - "(expected switch state: unknown state %s)", - bindtype, (const char *)split->items[1]); - } - 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); + 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); } struct cmd_results *cmd_bindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false, false); + return cmd_bindsym_or_bindcode(argc, argv, false); } struct cmd_results *cmd_bindcode(int argc, char **argv) { - 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); + return cmd_bindsym_or_bindcode(argc, argv, true); } /** * Execute the command associated to a binding */ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { - if (!config->active) { - sway_log(SWAY_DEBUG, "deferring command for binding: %s", - binding->command); - struct sway_binding *deferred = calloc(1, sizeof(struct sway_binding)); - if (!deferred) { - sway_log(SWAY_ERROR, "Failed to allocate deferred binding"); - return; - } - memcpy(deferred, binding, sizeof(struct sway_binding)); - deferred->command = binding->command ? strdup(binding->command) : NULL; - list_add(seat->deferred_bindings, deferred); - return; - } - sway_log(SWAY_DEBUG, "running command for binding: %s", binding->command); + struct sway_container *con = NULL; if (binding->type == BINDING_MOUSESYM || binding->type == BINDING_MOUSECODE) { @@ -647,118 +359,4 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) if (success) { ipc_event_binding(binding); } - - transaction_commit_dirty(); -} - -/** - * 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 %" PRIu32 " 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/border.c b/sway/commands/border.c index 7818fc962..6be5b7948 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -19,11 +19,11 @@ static void set_border(struct sway_container *con, view_set_csd_from_server(con->view, false); } else if (!con->view->using_csd && new_border == B_CSD) { view_set_csd_from_server(con->view, true); - con->saved_border = con->pending.border; + con->saved_border = con->border; } } if (new_border != B_CSD || container_is_floating(con)) { - con->pending.border = new_border; + con->border = new_border; } if (con->view) { con->view->using_csd = new_border == B_CSD; @@ -35,7 +35,7 @@ static void border_toggle(struct sway_container *con) { set_border(con, B_NONE); return; } - switch (con->pending.border) { + switch (con->border) { case B_NONE: set_border(con, B_PIXEL); break; @@ -75,7 +75,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { } else if (strcmp(argv[0], "pixel") == 0) { set_border(container, B_PIXEL); } else if (strcmp(argv[0], "csd") == 0) { - if (!view->xdg_decoration) { + if (!view || !view->xdg_decoration) { return cmd_results_new(CMD_INVALID, "This window doesn't support client side decorations"); } @@ -88,7 +88,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { "or 'border pixel '"); } if (argc == 2) { - container->pending.border_thickness = atoi(argv[1]); + container->border_thickness = atoi(argv[1]); } if (container_is_floating(container)) { diff --git a/sway/commands/client.c b/sway/commands/client.c index fd2ac7a87..f5c7d90fb 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -3,90 +3,117 @@ #include "sway/config.h" #include "sway/output.h" #include "sway/tree/container.h" -#include "util.h" -static void container_update_iterator(struct sway_container *con, void *data) { - container_update(con); +static void rebuild_textures_iterator(struct sway_container *con, void *data) { + container_update_marks_textures(con); + container_update_title_textures(con); } -static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, - struct border_colors *class, const char *default_indicator) { +/** + * Parse the hex string into an integer. + */ +static bool parse_color_int(char *hexstring, uint32_t *dest) { + if (hexstring[0] != '#') { + return false; + } + + if (strlen(hexstring) != 7 && strlen(hexstring) != 9) { + return false; + } + + ++hexstring; + char *end; + uint32_t decimal = strtol(hexstring, &end, 16); + + if (*end != '\0') { + return false; + } + + if (strlen(hexstring) == 6) { + // Add alpha + decimal = (decimal << 8) | 0xff; + } + + *dest = decimal; + return true; +} + +/** + * Parse the hex string into a float value. + */ +static bool parse_color_float(char *hexstring, float dest[static 4]) { + uint32_t decimal; + if (!parse_color_int(hexstring, &decimal)) { + return false; + } + dest[0] = ((decimal >> 24) & 0xff) / 255.0; + dest[1] = ((decimal >> 16) & 0xff) / 255.0; + dest[2] = ((decimal >> 8) & 0xff) / 255.0; + dest[3] = (decimal & 0xff) / 255.0; + return true; +} + +static struct cmd_results *handle_command(int argc, char **argv, + struct border_colors *class, char *cmd_name) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 3)) || - (error = checkarg(argc, cmd_name, EXPECTED_AT_MOST, 5))) { + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 5))) { return error; } - if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) { - sway_log(SWAY_ERROR, - "Warning: indicator and child_border colors have no effect for %s", - cmd_name); + if (!parse_color_float(argv[0], class->border)) { + return cmd_results_new(CMD_INVALID, + "Unable to parse border color '%s'", argv[0]); } - struct border_colors colors = {0}; - const char *ind_hex = argc > 3 ? argv[3] : default_indicator; - const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background - - struct { - const char *name; - const char *hex; - float *rgba[4]; - } properties[] = { - { "border", argv[0], colors.border }, - { "background", argv[1], colors.background }, - { "text", argv[2], colors.text }, - { "indicator", ind_hex, colors.indicator }, - { "child_border", child_hex, colors.child_border } - }; - for (size_t i = 0; i < sizeof(properties) / sizeof(properties[0]); i++) { - uint32_t color; - if (!parse_color(properties[i].hex, &color)) { - return cmd_results_new(CMD_INVALID, "Invalid %s color %s", - properties[i].name, properties[i].hex); - } - color_to_rgba(*properties[i].rgba, color); + if (!parse_color_float(argv[1], class->background)) { + return cmd_results_new(CMD_INVALID, + "Unable to parse background color '%s'", argv[1]); } - memcpy(class, &colors, sizeof(struct border_colors)); + if (!parse_color_float(argv[2], class->text)) { + return cmd_results_new(CMD_INVALID, + "Unable to parse text color '%s'", argv[2]); + } + + if (!parse_color_float(argv[3], class->indicator)) { + return cmd_results_new(CMD_INVALID, + "Unable to parse indicator color '%s'", argv[3]); + } + + if (!parse_color_float(argv[4], class->child_border)) { + return cmd_results_new(CMD_INVALID, + "Unable to parse child border color '%s'", argv[4]); + } if (config->active) { - root_for_each_container(container_update_iterator, NULL); + root_for_each_container(rebuild_textures_iterator, NULL); + + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole(output); + } } return cmd_results_new(CMD_SUCCESS, NULL); } struct cmd_results *cmd_client_focused(int argc, char **argv) { - return handle_command(argc, argv, "client.focused", - &config->border_colors.focused, "#2e9ef4ff"); + return handle_command(argc, argv, &config->border_colors.focused, "client.focused"); } struct cmd_results *cmd_client_focused_inactive(int argc, char **argv) { - return handle_command(argc, argv, "client.focused_inactive", - &config->border_colors.focused_inactive, "#484e50ff"); + return handle_command(argc, argv, &config->border_colors.focused_inactive, "client.focused_inactive"); } struct cmd_results *cmd_client_unfocused(int argc, char **argv) { - return handle_command(argc, argv, "client.unfocused", - &config->border_colors.unfocused, "#292d2eff"); + return handle_command(argc, argv, &config->border_colors.unfocused, "client.unfocused"); } struct cmd_results *cmd_client_urgent(int argc, char **argv) { - return handle_command(argc, argv, "client.urgent", - &config->border_colors.urgent, "#900000ff"); + return handle_command(argc, argv, &config->border_colors.urgent, "client.urgent"); } struct cmd_results *cmd_client_noop(int argc, char **argv) { sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); return cmd_results_new(CMD_SUCCESS, NULL); } - -struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) { - struct cmd_results *result = handle_command(argc, argv, - "client.focused_tab_title", - &config->border_colors.focused_tab_title, "#2e9ef4ff"); - if (result && result->status == CMD_SUCCESS) { - config->has_focused_tab_title = true; - } - return result; -} diff --git a/sway/commands/create_output.c b/sway/commands/create_output.c index 79283fd11..9961c8fb9 100644 --- a/sway/commands/create_output.c +++ b/sway/commands/create_output.c @@ -1,5 +1,4 @@ #include -#include #include #include #if WLR_HAS_X11_BACKEND @@ -18,9 +17,6 @@ static void create_output(struct wlr_backend *backend, void *data) { if (wlr_backend_is_wl(backend)) { wlr_wl_output_create(backend); *done = true; - } else if (wlr_backend_is_headless(backend)) { - wlr_headless_add_output(backend, 1920, 1080); - *done = true; } #if WLR_HAS_X11_BACKEND else if (wlr_backend_is_x11(backend)) { @@ -42,7 +38,7 @@ struct cmd_results *cmd_create_output(int argc, char **argv) { if (!done) { return cmd_results_new(CMD_INVALID, - "Can only create outputs for Wayland, X11 or headless backends"); + "Can only create outputs for Wayland or X11 backends"); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/exec.c b/sway/commands/exec.c index 2c6f3d2d5..87d907162 100644 --- a/sway/commands/exec.c +++ b/sway/commands/exec.c @@ -5,15 +5,12 @@ #include "stringop.h" struct cmd_results *cmd_exec(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = cmd_exec_validate(argc, argv))) { - return error; - } + if (!config->active) return cmd_results_new(CMD_DEFER, NULL); if (config->reloading) { char *args = join_args(argv, argc); sway_log(SWAY_DEBUG, "Ignoring 'exec %s' due to reload", args); free(args); return cmd_results_new(CMD_SUCCESS, NULL); } - return cmd_exec_process(argc, argv); + return cmd_exec_always(argc, argv); } diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index a966696c5..a57242950 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -6,31 +7,24 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/server.h" -#include "sway/desktop/launcher.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" #include "log.h" #include "stringop.h" -struct cmd_results *cmd_exec_validate(int argc, char **argv) { +struct cmd_results *cmd_exec_always(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { - return error; - } if (!config->active || config->validating) { return cmd_results_new(CMD_DEFER, NULL); } - return error; -} + if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { + return error; + } -struct cmd_results *cmd_exec_process(int argc, char **argv) { - struct cmd_results *error = NULL; - char *cmd = NULL; - bool no_startup_id = false; + char *tmp = NULL; if (strcmp(argv[0], "--no-startup-id") == 0) { - no_startup_id = true; + sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); --argc; ++argv; if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { return error; @@ -38,52 +32,63 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { } if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { - cmd = strdup(argv[0]); - strip_quotes(cmd); + tmp = strdup(argv[0]); + strip_quotes(tmp); } else { - cmd = join_args(argv, argc); + tmp = join_args(argv, argc); } + // Put argument into cmd array + char cmd[4096]; + strncpy(cmd, tmp, sizeof(cmd) - 1); + cmd[sizeof(cmd) - 1] = 0; + free(tmp); sway_log(SWAY_DEBUG, "Executing %s", cmd); - struct launcher_ctx *ctx = launcher_ctx_create_internal(); + int fd[2]; + if (pipe(fd) != 0) { + sway_log(SWAY_ERROR, "Unable to create pipe for fork"); + } + pid_t pid, child; // Fork process - pid_t child = fork(); - if (child == 0) { + if ((pid = fork()) == 0) { + // Fork child process again setsid(); - - if (ctx) { - const char *token = launcher_ctx_get_token_name(ctx); - setenv("XDG_ACTIVATION_TOKEN", token, 1); - if (!no_startup_id) { - setenv("DESKTOP_STARTUP_ID", token, 1); - } + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + close(fd[0]); + if ((child = fork()) == 0) { + close(fd[1]); + execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); + _exit(0); } - - execlp("sh", "sh", "-c", cmd, (void*)NULL); - sway_log_errno(SWAY_ERROR, "execve failed"); + ssize_t s = 0; + while ((size_t)s < sizeof(pid_t)) { + s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s); + } + close(fd[1]); _exit(0); // Close child process - } else if (child < 0) { - launcher_ctx_destroy(ctx); - free(cmd); + } else if (pid < 0) { + close(fd[0]); + close(fd[1]); return cmd_results_new(CMD_FAILURE, "fork() failed"); } - - sway_log(SWAY_DEBUG, "Child process created with pid %d", child); - if (ctx != NULL) { - sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); - ctx->pid = child; + close(fd[1]); // close write + ssize_t s = 0; + while ((size_t)s < sizeof(pid_t)) { + s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s); + } + close(fd[0]); + // cleanup child process + waitpid(pid, NULL, 0); + if (child > 0) { + sway_log(SWAY_DEBUG, "Child process created with pid %d", child); + root_record_workspace_pid(child); + } else { + return cmd_results_new(CMD_FAILURE, "Second fork() failed"); } - free(cmd); return cmd_results_new(CMD_SUCCESS, NULL); } - -struct cmd_results *cmd_exec_always(int argc, char **argv) { - struct cmd_results *error; - if ((error = cmd_exec_validate(argc, argv))) { - return error; - } - return cmd_exec_process(argc, argv); -} diff --git a/sway/commands/exit.c b/sway/commands/exit.c index 0f326cea6..10cde640c 100644 --- a/sway/commands/exit.c +++ b/sway/commands/exit.c @@ -1,7 +1,8 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/server.h" + +void sway_terminate(int exit_code); struct cmd_results *cmd_exit(int argc, char **argv) { struct cmd_results *error = NULL; diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 74f6522c7..821093695 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -32,16 +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)) { - while (container->pending.parent) { - container = container->pending.parent; + while (container->parent) { + container = container->parent; } } @@ -50,10 +45,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); - // Floating containers in the scratchpad should be ignored - if (container->pending.workspace) { - arrange_workspace(container->pending.workspace); - } + arrange_workspace(container->workspace); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c index e8c24ace2..3a1d606a0 100644 --- a/sway/commands/floating_minmax_size.c +++ b/sway/commands/floating_minmax_size.c @@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, char *err; int width = (int)strtol(argv[0], &err, 10); if (*err) { - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, cmd_name, usage); } if (strcmp(argv[1], "x") != 0) { - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, cmd_name, usage); } int height = (int)strtol(argv[2], &err, 10); if (*err) { - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, cmd_name, usage); } *config_width = width; diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c index c02bce978..fd606281a 100644 --- a/sway/commands/floating_modifier.c +++ b/sway/commands/floating_modifier.c @@ -9,11 +9,6 @@ struct cmd_results *cmd_floating_modifier(int argc, char **argv) { return error; } - if (strcasecmp(argv[0], "none") == 0) { - config->floating_mod = 0; - return cmd_results_new(CMD_SUCCESS, NULL); - } - uint32_t mod = get_modifier_mask_by_name(argv[0]); if (!mod) { return cmd_results_new(CMD_INVALID, "Invalid modifier"); diff --git a/sway/commands/focus.c b/sway/commands/focus.c index facd82de7..79b2f551f 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -1,4 +1,3 @@ -#include #include #include #include "log.h" @@ -14,50 +13,6 @@ #include "stringop.h" #include "util.h" -static bool get_direction_from_next_prev(struct sway_container *container, - struct sway_seat *seat, const char *name, enum wlr_direction *out) { - enum sway_container_layout parent_layout = L_NONE; - if (container) { - parent_layout = container_parent_layout(container); - } - - if (strcasecmp(name, "prev") == 0) { - switch (parent_layout) { - case L_HORIZ: - case L_TABBED: - *out = WLR_DIRECTION_LEFT; - break; - case L_VERT: - case L_STACKED: - *out = WLR_DIRECTION_UP; - break; - case L_NONE: - return true; - default: - return false; - } - } else if (strcasecmp(name, "next") == 0) { - switch (parent_layout) { - case L_HORIZ: - case L_TABBED: - *out = WLR_DIRECTION_RIGHT; - break; - case L_VERT: - case L_STACKED: - *out = WLR_DIRECTION_DOWN; - break; - case L_NONE: - return true; - default: - return false; - } - } else { - return false; - } - - return true; -} - static bool parse_direction(const char *name, enum wlr_direction *out) { if (strcasecmp(name, "left") == 0) { @@ -82,9 +37,6 @@ static struct sway_node *get_node_in_output_direction( struct sway_output *output, enum wlr_direction dir) { struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *ws = output_get_active_workspace(output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return NULL; - } if (ws->fullscreen) { return seat_get_focus_inactive(seat, &ws->fullscreen->node); } @@ -135,15 +87,14 @@ static struct sway_node *get_node_in_output_direction( return &ws->node; } -static struct sway_node *node_get_in_direction_tiling( - struct sway_container *container, struct sway_seat *seat, - enum wlr_direction dir, bool descend) { +static struct sway_node *node_get_in_direction(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) { - if (current->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { + if (current->fullscreen_mode == FULLSCREEN_WORKSPACE) { // Fullscreen container with a direction - go straight to outputs - struct sway_output *output = current->pending.workspace->output; + struct sway_output *output = current->workspace->output; struct sway_output *new_output = output_get_in_direction(output, dir); if (!new_output) { @@ -151,7 +102,7 @@ static struct sway_node *node_get_in_direction_tiling( } return get_node_in_output_direction(new_output, dir); } - if (current->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { + if (current->fullscreen_mode == FULLSCREEN_GLOBAL) { return NULL; } @@ -192,24 +143,19 @@ static struct sway_node *node_get_in_direction_tiling( } } else { struct sway_container *desired_con = siblings->items[desired]; - if (!descend) { - return &desired_con->node; - } else { - struct sway_container *c = seat_get_focus_inactive_view( - seat, &desired_con->node); - return &c->node; - } + struct sway_container *c = seat_get_focus_inactive_view( + seat, &desired_con->node); + return &c->node; } } - current = current->pending.parent; + current = current->parent; } // Check a different output - struct sway_output *output = container->pending.workspace->output; + struct sway_output *output = container->workspace->output; struct sway_output *new_output = output_get_in_direction(output, dir); - if ((config->focus_wrapping != WRAP_WORKSPACE || - container->node.type == N_WORKSPACE) && new_output) { + if (new_output) { return get_node_in_output_direction(new_output, dir); } @@ -223,41 +169,6 @@ static struct sway_node *node_get_in_direction_tiling( 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->pending.x + con->pending.width / 2; - double ref_ly = con->pending.y + con->pending.height / 2; - double closest_distance = DBL_MAX; - struct sway_container *closest_con = NULL; - - if (!con->pending.workspace) { - return NULL; - } - - for (int i = 0; i < con->pending.workspace->floating->length; i++) { - struct sway_container *floater = con->pending.workspace->floating->items[i]; - if (floater == con) { - continue; - } - float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT - ? (floater->pending.x + floater->pending.width / 2) - ref_lx - : (floater->pending.y + floater->pending.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; @@ -267,25 +178,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, new_focus = seat_get_focus_inactive_tiling(seat, ws); } if (new_focus) { - struct sway_container *new_focus_view = - seat_get_focus_inactive_view(seat, &new_focus->node); - if (new_focus_view) { - new_focus = new_focus_view; - } seat_set_focus_container(seat, new_focus); - - // If we're on the floating layer and the floating container area - // overlaps the position on the tiling layer that would be warped to, - // `seat_consider_warp_to_focus` would decide not to warp, but we need - // to anyway. - if (config->mouse_warping == WARP_CONTAINER) { - cursor_warp_to_container(seat->cursor, new_focus, true); - } else { - seat_consider_warp_to_focus(seat); - } + seat_consider_warp_to_focus(seat); } else { return cmd_results_new(CMD_FAILURE, - "Failed to find a %s container in workspace.", + "Failed to find a %s container in workspace", floating ? "floating" : "tiling"); } return cmd_results_new(CMD_SUCCESS, NULL); @@ -295,7 +192,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, int argc, char **argv) { if (!argc) { return cmd_results_new(CMD_INVALID, - "Expected 'focus output '."); + "Expected 'focus output '"); } char *identifier = join_args(argv, argc); struct sway_output *output = output_by_name_or_id(identifier); @@ -305,14 +202,9 @@ static struct cmd_results *focus_output(struct sway_seat *seat, if (!parse_direction(identifier, &direction)) { free(identifier); return cmd_results_new(CMD_INVALID, - "There is no output with that name."); + "There is no output with that name"); } struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (!ws) { - free(identifier); - return cmd_results_new(CMD_FAILURE, - "No focused workspace to base directions off of."); - } output = output_get_in_direction(ws->output, direction); if (!output) { @@ -339,7 +231,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, static struct cmd_results *focus_parent(void) { struct sway_seat *seat = config->handler_context.seat; struct sway_container *con = config->handler_context.container; - if (!con || con->pending.fullscreen_mode) { + if (!con || con->fullscreen_mode) { return cmd_results_new(CMD_SUCCESS, NULL); } struct sway_node *parent = node_get_parent(&con->node); @@ -375,27 +267,15 @@ struct cmd_results *cmd_focus(int argc, char **argv) { struct sway_seat *seat = config->handler_context.seat; if (node->type < N_WORKSPACE) { return cmd_results_new(CMD_FAILURE, - "Command 'focus' cannot be used above the workspace level."); + "Command 'focus' cannot be used above the workspace level"); } - if (argc == 0) { - if (!container) { - return cmd_results_new(CMD_FAILURE, "No container to focus was specified."); - } - - if (container_is_scratchpad_hidden_or_child(container)) { + if (argc == 0 && container) { + if (container_is_scratchpad_hidden(container)) { root_scratchpad_show(container); } - // if we are switching to a container under a fullscreen window, we first - // need to exit fullscreen so that the newly focused container becomes visible - struct sway_container *obstructing = container_obstructing_fullscreen_container(container); - if (obstructing) { - container_fullscreen_disable(obstructing); - arrange_root(); - } seat_set_focus_container(seat, container); seat_consider_warp_to_focus(seat); - container_raise_floating(container); return cmd_results_new(CMD_SUCCESS, NULL); } @@ -421,19 +301,10 @@ struct cmd_results *cmd_focus(int argc, char **argv) { } enum wlr_direction direction = 0; - bool descend = true; if (!parse_direction(argv[0], &direction)) { - if (!get_direction_from_next_prev(container, seat, argv[0], &direction)) { - return cmd_results_new(CMD_INVALID, - "Expected 'focus ' " - "or 'focus output '"); - } else if (argc == 2 && strcasecmp(argv[1], "sibling") == 0) { - descend = false; - } - } - - if (!direction) { - return cmd_results_new(CMD_SUCCESS, NULL); + return cmd_results_new(CMD_INVALID, + "Expected 'focus ' " + "or 'focus output '"); } if (node->type == N_WORKSPACE) { @@ -451,23 +322,11 @@ struct cmd_results *cmd_focus(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL); } - if (!sway_assert(container, "Expected container to be non null")) { - return cmd_results_new(CMD_FAILURE, ""); - } - struct sway_node *next_focus = NULL; - if (container_is_floating(container) && - container->pending.fullscreen_mode == FULLSCREEN_NONE) { - next_focus = node_get_in_direction_floating(container, seat, direction); - } else { - next_focus = node_get_in_direction_tiling(container, seat, direction, descend); - } + struct sway_node *next_focus = + node_get_in_direction(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/focus_wrapping.c b/sway/commands/focus_wrapping.c index d3901cb8f..3a74a3ea5 100644 --- a/sway/commands/focus_wrapping.c +++ b/sway/commands/focus_wrapping.c @@ -11,8 +11,6 @@ struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { if (strcasecmp(argv[0], "force") == 0) { config->focus_wrapping = WRAP_FORCE; - } else if (strcasecmp(argv[0], "workspace") == 0) { - config->focus_wrapping = WRAP_WORKSPACE; } else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) { config->focus_wrapping = WRAP_YES; } else { diff --git a/sway/commands/font.c b/sway/commands/font.c index 842e8ae61..c54365b53 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -1,9 +1,9 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" #include "log.h" #include "stringop.h" -#include struct cmd_results *cmd_font(int argc, char **argv) { struct cmd_results *error = NULL; @@ -13,37 +13,15 @@ struct cmd_results *cmd_font(int argc, char **argv) { char *font = join_args(argv, argc); free(config->font); - if (has_prefix(font, "pango:")) { + if (strncmp(font, "pango:", 6) == 0) { config->pango_markup = true; - config->font = strdup(font + strlen("pango:")); - free(font); + config->font = strdup(font + 6); } else { config->pango_markup = false; - config->font = font; + config->font = strdup(font); } - // Parse the font early so we can reject it if it's not valid for pango. - // Also avoids re-parsing each time we render text. - PangoFontDescription *font_description = pango_font_description_from_string(config->font); - - const char *family = pango_font_description_get_family(font_description); - if (family == NULL) { - pango_font_description_free(font_description); - return cmd_results_new(CMD_FAILURE, "Invalid font family."); - } - - const gint size = pango_font_description_get_size(font_description); - if (size == 0) { - pango_font_description_free(font_description); - return cmd_results_new(CMD_FAILURE, "Invalid font size."); - } - - if (config->font_description != NULL) { - pango_font_description_free(config->font_description); - } - - config->font_description = font_description; - config_update_font_height(); - + free(font); + config_update_font_height(true); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index 905e67767..63cba0999 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c @@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, "%s", err_str); + error = cmd_results_new(CMD_INVALID, err_str); free(err_str); return error; } @@ -22,14 +22,6 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { criteria->type = CT_COMMAND; criteria->cmdlist = join_args(argv + 1, argc - 1); - // Check if it already exists - if (criteria_already_exists(criteria)) { - sway_log(SWAY_DEBUG, "for_window already exists: '%s' -> '%s'", - criteria->raw, criteria->cmdlist); - criteria_destroy(criteria); - return cmd_results_new(CMD_SUCCESS, NULL); - } - list_add(config->criteria, criteria); sway_log(SWAY_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); diff --git a/sway/commands/force_display_urgency_hint.c b/sway/commands/force_display_urgency_hint.c index 3d432cba9..3202fc43a 100644 --- a/sway/commands/force_display_urgency_hint.c +++ b/sway/commands/force_display_urgency_hint.c @@ -1,6 +1,5 @@ #include "sway/commands.h" #include "sway/config.h" -#include struct cmd_results *cmd_force_display_urgency_hint(int argc, char **argv) { struct cmd_results *error = NULL; @@ -9,16 +8,13 @@ struct cmd_results *cmd_force_display_urgency_hint(int argc, char **argv) { return error; } - errno = 0; - char *end; - int timeout = (int)strtol(argv[0], &end, 10); - if (errno || end == argv[0] || (*end && strcmp(end, "ms") != 0)) { - return cmd_results_new(CMD_INVALID, "timeout integer invalid"); - } - - if (argc > 1 && strcmp(argv[1], "ms") != 0) { - return cmd_results_new(CMD_INVALID, - "Expected 'force_display_urgency_hint [ms]'"); + char *err; + int timeout = (int)strtol(argv[0], &err, 10); + if (*err) { + if (strcmp(err, "ms") != 0) { + return cmd_results_new(CMD_INVALID, + "Expected 'force_display_urgency_hint ms'"); + } } config->urgent_timeout = timeout > 0 ? timeout : 0; diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c index fafc1c3ef..e646ae9b7 100644 --- a/sway/commands/force_focus_wrapping.c +++ b/sway/commands/force_focus_wrapping.c @@ -1,16 +1,9 @@ +#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 21c1e9a09..52248ce4a 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -18,19 +18,16 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Can't run this command while there's no outputs connected."); } + struct sway_node *node = config->handler_context.node; struct sway_container *container = config->handler_context.container; - - if (!container) { - // If the focus is not a container, do nothing successfully - return cmd_results_new(CMD_SUCCESS, NULL); - } else if (!container->pending.workspace) { - // If in the scratchpad, operate on the highest container - while (container->pending.parent) { - container = container->pending.parent; - } + struct sway_workspace *workspace = config->handler_context.workspace; + if (node->type == N_WORKSPACE && workspace->tiling->length == 0) { + return cmd_results_new(CMD_FAILURE, + "Can't fullscreen an empty workspace"); } - bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE; + bool is_fullscreen = container && + container->fullscreen_mode != FULLSCREEN_NONE; bool global = false; bool enable = !is_fullscreen; @@ -46,6 +43,13 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { global = strcasecmp(argv[1], "global") == 0; } + if (enable && node->type == N_WORKSPACE) { + // Wrap the workspace's children in a container so we can fullscreen it + container = workspace_wrap_children(workspace); + workspace->layout = L_HORIZ; + seat_set_focus_container(config->handler_context.seat, container); + } + enum sway_fullscreen_mode mode = FULLSCREEN_NONE; if (enable) { mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 7ac6fcff5..021df843a 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -11,8 +11,7 @@ enum gaps_op { GAPS_OP_SET, GAPS_OP_ADD, - GAPS_OP_SUBTRACT, - GAPS_OP_TOGGLE + GAPS_OP_SUBTRACT }; struct gaps_data { @@ -103,9 +102,6 @@ static void apply_gaps_op(int *prop, enum gaps_op op, int amount) { case GAPS_OP_SUBTRACT: *prop -= amount; break; - case GAPS_OP_TOGGLE: - *prop = *prop ? 0 : amount; - break; } } @@ -137,9 +133,9 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) { } // gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all -// set|plus|minus|toggle +// set|plus|minus static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" - "top|right|bottom|left current|all set|plus|minus|toggle '"; + "top|right|bottom|left current|all set|plus|minus '"; static struct cmd_results *gaps_set_runtime(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); if (error) { @@ -184,8 +180,6 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { data.operation = GAPS_OP_ADD; } else if (strcasecmp(argv[2], "minus") == 0) { data.operation = GAPS_OP_SUBTRACT; - } else if (strcasecmp(argv[2], "toggle") == 0) { - data.operation = GAPS_OP_TOGGLE; } else { return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); } @@ -206,7 +200,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { } // gaps inner|outer|| - sets defaults for workspaces -// gaps inner|outer|| current|all set|plus|minus|toggle - runtime only +// gaps inner|outer|| current|all set|plus|minus - runtime only // = horizontal|vertical // = top|right|bottom|left struct cmd_results *cmd_gaps(int argc, char **argv) { @@ -215,13 +209,15 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return error; } + bool config_loading = !config->active || config->reloading; + if (argc == 2) { return gaps_set_defaults(argc, argv); } - if (argc == 4 && !config->reading) { + if (argc == 4 && !config_loading) { return gaps_set_runtime(argc, argv); } - if (config->reading) { + if (config_loading) { return cmd_results_new(CMD_INVALID, "Expected %s", expected_defaults); } return cmd_results_new(CMD_INVALID, "Expected %s or %s", diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c deleted file mode 100644 index 8dff29a34..000000000 --- a/sway/commands/gesture.c +++ /dev/null @@ -1,164 +0,0 @@ -#include "sway/config.h" - -#include "gesture.h" -#include "log.h" -#include "stringop.h" -#include "sway/commands.h" - -void free_gesture_binding(struct sway_gesture_binding *binding) { - if (!binding) { - return; - } - free(binding->input); - free(binding->command); - free(binding); -} - -/** - * Returns true if the bindings have the same gesture type, direction, etc - */ -static bool binding_gesture_equal(struct sway_gesture_binding *binding_a, - struct sway_gesture_binding *binding_b) { - if (strcmp(binding_a->input, binding_b->input) != 0) { - return false; - } - - if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) { - return false; - } - - if ((binding_a->flags & BINDING_EXACT) != - (binding_b->flags & BINDING_EXACT)) { - return false; - } - return true; -} - -/** - * Add gesture binding to config - */ -static struct cmd_results *gesture_binding_add( - struct sway_gesture_binding *binding, - const char *gesturecombo, bool warn) { - list_t *mode_bindings = config->current_mode->gesture_bindings; - // overwrite the binding if it already exists - bool overwritten = false; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_gesture_binding *config_binding = mode_bindings->items[i]; - if (binding_gesture_equal(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", - gesturecombo, binding->command, config_binding->command); - if (warn) { - config_add_swaynag_warning("Overwriting binding" - "'%s' to `%s` from `%s`", - gesturecombo, binding->command, - config_binding->command); - } - free_gesture_binding(config_binding); - mode_bindings->items[i] = binding; - overwritten = true; - } - } - - if (!overwritten) { - list_add(mode_bindings, binding); - sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`", - gesturecombo, binding->command); - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} - -/** - * Remove gesture binding from config - */ -static struct cmd_results *gesture_binding_remove( - struct sway_gesture_binding *binding, const char *gesturecombo) { - list_t *mode_bindings = config->current_mode->gesture_bindings; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_gesture_binding *config_binding = mode_bindings->items[i]; - if (binding_gesture_equal(binding, config_binding)) { - free_gesture_binding(config_binding); - free_gesture_binding(binding); - list_del(mode_bindings, i); - sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture", - gesturecombo); - return cmd_results_new(CMD_SUCCESS, NULL); - } - } - - free_gesture_binding(binding); - return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`", - gesturecombo); -} - -/** - * Parse and execute bindgesture or unbindgesture command. - */ -static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) { - int minargs = 2; - char *bindtype = "bindgesture"; - if (unbind) { - minargs--; - bindtype = "unbindgesture"; - } - - struct cmd_results *error = NULL; - if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { - return error; - } - struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding)); - if (!binding) { - return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); - } - binding->input = strdup("*"); - - bool warn = true; - - // Handle flags - while (argc > 0) { - if (strcmp("--exact", argv[0]) == 0) { - binding->flags |= BINDING_EXACT; - } else if (strcmp("--no-warn", argv[0]) == 0) { - warn = false; - } else if (has_prefix(argv[0], "--input-device=")) { - free(binding->input); - binding->input = strdup(argv[0] + strlen("--input-device=")); - } else { - break; - } - argv++; - argc--; - } - - if (argc < minargs) { - free(binding); - return cmd_results_new(CMD_FAILURE, - "Invalid %s command (expected at least %d " - "non-option arguments, got %d)", bindtype, minargs, argc); - } - - char* errmsg = NULL; - if ((errmsg = gesture_parse(argv[0], &binding->gesture))) { - free(binding); - struct cmd_results *final = cmd_results_new(CMD_FAILURE, - "Invalid %s command (%s)", - bindtype, errmsg); - free(errmsg); - return final; - } - - if (unbind) { - return gesture_binding_remove(binding, argv[0]); - } - binding->command = join_args(argv + 1, argc - 1); - return gesture_binding_add(binding, argv[0], warn); -} - -struct cmd_results *cmd_bindgesture(int argc, char **argv) { - return cmd_bind_or_unbind_gesture(argc, argv, false); -} - -struct cmd_results *cmd_unbindgesture(int argc, char **argv) { - return cmd_bind_or_unbind_gesture(argc, argv, true); -} diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 43bd6dc86..84a217b89 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -4,25 +4,11 @@ #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_AT_LEAST, 1))) { + if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 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, "%s", expected_syntax); - } - if (strcmp(argv[0], "none") == 0) { config->hide_edge_borders = E_NONE; } else if (strcmp(argv[0], "vertical") == 0) { @@ -32,15 +18,14 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { } else if (strcmp(argv[0], "both") == 0) { config->hide_edge_borders = E_BOTH; } else if (strcmp(argv[0], "smart") == 0) { - config->hide_edge_borders = E_NONE; - config->hide_edge_borders_smart = ESMART_ON; + config->hide_edge_borders = E_SMART; } else if (strcmp(argv[0], "smart_no_gaps") == 0) { - config->hide_edge_borders = E_NONE; - config->hide_edge_borders_smart = ESMART_NO_GAPS; + config->hide_edge_borders = E_SMART_NO_GAPS; } else { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, "Expected 'hide_edge_borders " + "'"); } - 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 e0d0c0640..d18098564 100644 --- a/sway/commands/include.c +++ b/sway/commands/include.c @@ -3,12 +3,15 @@ struct cmd_results *cmd_include(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "include", EXPECTED_AT_LEAST, 1))) { + if ((error = checkarg(argc, "include", EXPECTED_EQUAL_TO, 1))) { return error; } - char *files = join_args(argv, argc); - // We don't care if the included config(s) fails to load. - load_include_configs(files, config, &config->swaynag_config_errors); + + 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]); + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c deleted file mode 100644 index 6125736ac..000000000 --- a/sway/commands/inhibit_idle.c +++ /dev/null @@ -1,51 +0,0 @@ -#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(); - } - } 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 310375a94..b72bd76ba 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -2,49 +2,39 @@ #include #include "sway/commands.h" #include "sway/input/input-manager.h" -#include "sway/input/keyboard.h" #include "log.h" #include "stringop.h" // must be in order for the bsearch -static const struct cmd_handler input_handlers[] = { +static struct cmd_handler input_handlers[] = { { "accel_profile", input_cmd_accel_profile }, - { "calibration_matrix", input_cmd_calibration_matrix }, { "click_method", input_cmd_click_method }, - { "clickfinger_button_map", input_cmd_clickfinger_button_map }, { "drag", input_cmd_drag }, { "drag_lock", input_cmd_drag_lock }, { "dwt", input_cmd_dwt }, - { "dwtp", input_cmd_dwtp }, { "events", input_cmd_events }, { "left_handed", input_cmd_left_handed }, { "map_from_region", input_cmd_map_from_region }, { "map_to_output", input_cmd_map_to_output }, - { "map_to_region", input_cmd_map_to_region }, { "middle_emulation", input_cmd_middle_emulation }, { "natural_scroll", input_cmd_natural_scroll }, { "pointer_accel", input_cmd_pointer_accel }, { "repeat_delay", input_cmd_repeat_delay }, { "repeat_rate", input_cmd_repeat_rate }, - { "rotation_angle", input_cmd_rotation_angle }, { "scroll_button", input_cmd_scroll_button }, - { "scroll_button_lock", input_cmd_scroll_button_lock }, { "scroll_factor", input_cmd_scroll_factor }, { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, { "tap_button_map", input_cmd_tap_button_map }, - { "tool_mode", input_cmd_tool_mode }, - { "xkb_file", input_cmd_xkb_file }, { "xkb_layout", input_cmd_xkb_layout }, { "xkb_model", input_cmd_xkb_model }, { "xkb_options", input_cmd_xkb_options }, { "xkb_rules", input_cmd_xkb_rules }, - { "xkb_switch_layout", input_cmd_xkb_switch_layout }, { "xkb_variant", input_cmd_xkb_variant }, }; // must be in order for the bsearch -static const struct cmd_handler input_config_handlers[] = { +static struct cmd_handler input_config_handlers[] = { { "xkb_capslock", input_cmd_xkb_capslock }, { "xkb_numlock", input_cmd_xkb_numlock }, }; @@ -78,25 +68,11 @@ struct cmd_results *cmd_input(int argc, char **argv) { input_handlers, sizeof(input_handlers)); } - if ((!res || res->status == CMD_SUCCESS) && - strcmp(argv[1], "xkb_switch_layout") != 0) { - char *error = NULL; + if (!res || res->status == CMD_SUCCESS) { struct input_config *ic = - store_input_config(config->handler_context.input_config, &error); - if (!ic) { - free_input_config(config->handler_context.input_config); - if (res) { - free_cmd_results(res); - } - res = cmd_results_new(CMD_FAILURE, "Failed to compile keymap: %s", - error ? error : "(details unavailable)"); - free(error); - return res; - } + store_input_config(config->handler_context.input_config); - if (!config->reading) { - input_manager_apply_input_config(ic); - } + input_manager_apply_input_config(ic); } else { free_input_config(config->handler_context.input_config); } diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c deleted file mode 100644 index 53fe2c35e..000000000 --- a/sway/commands/input/calibration_matrix.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "sway/input/input-manager.h" -#include "log.h" -#include "stringop.h" -#include "util.h" - -struct cmd_results *input_cmd_calibration_matrix(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "calibration_matrix", EXPECTED_EQUAL_TO, 6))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - float parsed[6]; - for (int i = 0; i < argc; ++i) { - char *item = argv[i]; - float x = parse_float(item); - if (isnan(x)) { - return cmd_results_new(CMD_FAILURE, "calibration_matrix: unable to parse float: %s", item); - } - parsed[i] = x; - } - - ic->calibration_matrix.configured = true; - memcpy(ic->calibration_matrix.matrix, parsed, sizeof(ic->calibration_matrix.matrix)); - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c index 919c7cb7d..03fcb458f 100644 --- a/sway/commands/input/click_method.c +++ b/sway/commands/input/click_method.c @@ -23,7 +23,7 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) { ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; } else { return cmd_results_new(CMD_INVALID, - "Expected 'click_method '"); + "Expected 'click_method "); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/input/clickfinger_button_map.c b/sway/commands/input/clickfinger_button_map.c deleted file mode 100644 index 57d6e39a6..000000000 --- a/sway/commands/input/clickfinger_button_map.c +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "sway/input/input-manager.h" - -struct cmd_results *input_cmd_clickfinger_button_map(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "clickfinger_button_map", EXPECTED_AT_LEAST, 1))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - if (strcasecmp(argv[0], "lrm") == 0) { - ic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM; - } else if (strcasecmp(argv[0], "lmr") == 0) { - ic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR; - } else { - return cmd_results_new(CMD_INVALID, - "Expected 'clickfinger_button_map '"); - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 915a7ada1..24c548e2d 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -15,11 +15,6 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "No input device defined."); } -#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - if (strcmp(argv[0], "enabled_sticky") == 0) { - ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; - } else -#endif if (parse_boolean(argv[0], true)) { ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; } else { diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c deleted file mode 100644 index 232e2b261..000000000 --- a/sway/commands/input/dwtp.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "sway/input/input-manager.h" -#include "util.h" - -struct cmd_results *input_cmd_dwtp(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - if (parse_boolean(argv[0], true)) { - ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED; - } else { - ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED; - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 4650108a1..44bc9e74a 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -1,20 +1,14 @@ #include #include #include -#include +#include #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" -#include "sway/server.h" #include "log.h" -#if WLR_HAS_LIBINPUT_BACKEND -#include -#endif - static void toggle_supported_send_events_for_device(struct input_config *ic, struct sway_input_device *input_device) { -#if WLR_HAS_LIBINPUT_BACKEND struct wlr_input_device *wlr_device = input_device->wlr_device; if (!wlr_input_device_is_libinput(wlr_device)) { return; @@ -47,7 +41,6 @@ static void toggle_supported_send_events_for_device(struct input_config *ic, } ic->send_events = mode; -#endif } static int mode_for_name(const char *name) { @@ -63,7 +56,6 @@ static int mode_for_name(const char *name) { static void toggle_select_send_events_for_device(struct input_config *ic, struct sway_input_device *input_device, int argc, char **argv) { -#if WLR_HAS_LIBINPUT_BACKEND if (!wlr_input_device_is_libinput(input_device->wlr_device)) { return; } @@ -80,42 +72,39 @@ static void toggle_select_send_events_for_device(struct input_config *ic, } } ic->send_events = mode_for_name(argv[index % argc]); -#endif } -static void toggle_send_events(int argc, char **argv) { - struct input_config *ic = config->handler_context.input_config; - bool wildcard = strcmp(ic->identifier, "*") == 0; - const char *type = has_prefix(ic->identifier, "type:") - ? ic->identifier + strlen("type:") : NULL; - struct sway_input_device *device = NULL; - wl_list_for_each(device, &server.input->devices, link) { - if (wildcard || type) { - ic = new_input_config(device->identifier); - if (!ic) { - continue; +static void toggle_send_events(struct input_config *ic, int argc, char **argv) { + struct sway_input_device *input_device = NULL; + wl_list_for_each(input_device, &server.input->devices, link) { + if (strcmp(input_device->identifier, ic->identifier) == 0) { + if (argc) { + toggle_select_send_events_for_device(ic, input_device, + argc, argv); + } else { + toggle_supported_send_events_for_device(ic, input_device); } - if (type && strcmp(input_device_get_type(device), type) != 0) { - continue; - } - } else if (strcmp(ic->identifier, device->identifier) != 0) { - continue; - } - - if (argc) { - toggle_select_send_events_for_device(ic, device, argc, argv); - } else { - toggle_supported_send_events_for_device(ic, device); - } - - if (wildcard || type) { - store_input_config(ic, NULL); - } else { return; } } } +static void toggle_wildcard_send_events(int argc, char **argv) { + struct sway_input_device *input_device = NULL; + wl_list_for_each(input_device, &server.input->devices, link) { + struct input_config *ic = new_input_config(input_device->identifier); + if (!ic) { + break; + } + if (argc) { + toggle_select_send_events_for_device(ic, input_device, argc, argv); + } else { + toggle_supported_send_events_for_device(ic, input_device); + } + store_input_config(ic); + } +} + struct cmd_results *input_cmd_events(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { @@ -143,15 +132,15 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { "Invalid toggle mode %s", argv[i]); } } - - toggle_send_events(argc - 1, argv + 1); - - if (strcmp(ic->identifier, "*") == 0 || has_prefix(ic->identifier, "type:")) { - // Update the device input configs and then reset the type/wildcard + if (strcmp(ic->identifier, "*") == 0) { + // Update the device input configs and then reset the wildcard // config send events mode so that is does not override the device // ones. The device ones will be applied when attempting to apply - // the type/wildcard config + // the wildcard config + toggle_wildcard_send_events(argc - 1, argv + 1); ic->send_events = INT_MIN; + } else { + toggle_send_events(ic, argc - 1, argv + 1); } } else { return cmd_results_new(CMD_INVALID, diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index 2f8f753db..de00b7141 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -10,21 +11,11 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) { *mm = false; char *end; - - // Check for "0x" prefix to avoid strtod treating the string as hex - if (str[0] == '0' && str[1] == 'x') { - if (strlen(str) < 3) { - return false; - } - *x = 0; - end = (char *)str + 2; - } else { - *x = strtod(str, &end); - if (end[0] != 'x') { - return false; - } - ++end; + *x = strtod(str, &end); + if (end[0] != 'x') { + return false; } + ++end; *y = strtod(end, &end); if (end[0] == 'm') { diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c index a7266baac..6d6231867 100644 --- a/sway/commands/input/map_to_output.c +++ b/sway/commands/input/map_to_output.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/config.h" @@ -15,7 +16,6 @@ struct cmd_results *input_cmd_map_to_output(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "No input device defined."); } - ic->mapped_to = MAPPED_TO_OUTPUT; ic->mapped_to_output = strdup(argv[0]); return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c deleted file mode 100644 index 9087c5898..000000000 --- a/sway/commands/input/map_to_region.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include "sway/commands.h" -#include "sway/config.h" - -struct cmd_results *input_cmd_map_to_region(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "map_to_region", EXPECTED_EQUAL_TO, 4))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined"); - } - - ic->mapped_to = MAPPED_TO_REGION; - ic->mapped_to_region = calloc(1, sizeof(struct wlr_box)); - - const char *errstr; - char *end; - - ic->mapped_to_region->x = strtol(argv[0], &end, 10); - if (end[0] != '\0') { - errstr = "Invalid X coordinate"; - goto error; - } - - ic->mapped_to_region->y = strtol(argv[1], &end, 10); - if (end[0] != '\0') { - errstr = "Invalid Y coordinate"; - goto error; - } - - ic->mapped_to_region->width = strtol(argv[2], &end, 10); - if (end[0] != '\0' || ic->mapped_to_region->width < 1) { - errstr = "Invalid width"; - goto error; - } - - ic->mapped_to_region->height = strtol(argv[3], &end, 10); - if (end[0] != '\0' || ic->mapped_to_region->height < 1) { - errstr = "Invalid height"; - goto error; - } - - return cmd_results_new(CMD_SUCCESS, NULL); - -error: - free(ic->mapped_to_region); - ic->mapped_to_region = NULL; - return cmd_results_new(CMD_FAILURE, "%s", errstr); -} diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c deleted file mode 100644 index 5e278fff7..000000000 --- a/sway/commands/input/rotation_angle.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "sway/input/input-manager.h" -#include "util.h" - -struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "rotation_angle", EXPECTED_AT_LEAST, 1))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - float rotation_angle = parse_float(argv[0]); - if (isnan(rotation_angle)) { - return cmd_results_new(CMD_INVALID, - "Invalid rotation_angle; expected float."); - } if (rotation_angle < 0 || rotation_angle > 360) { - return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)"); - } - ic->rotation_angle = rotation_angle; - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c index 81f69a6df..6b3314190 100644 --- a/sway/commands/input/scroll_button.c +++ b/sway/commands/input/scroll_button.c @@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { char *message = NULL; uint32_t button = get_mouse_button(*argv, &message); if (message) { - error = cmd_results_new(CMD_INVALID, "%s", message); + error = cmd_results_new(CMD_INVALID, message); free(message); return error; } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c deleted file mode 100644 index f96b65145..000000000 --- a/sway/commands/input/scroll_button_lock.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "sway/input/input-manager.h" -#include "util.h" - -struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "scroll_button_lock", EXPECTED_AT_LEAST, 1))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - if (parse_boolean(argv[0], true)) { - ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED; - } else { - ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED; - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/input/tool_mode.c b/sway/commands/input/tool_mode.c deleted file mode 100644 index 04316857f..000000000 --- a/sway/commands/input/tool_mode.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" - -static void set_tool_mode(struct input_config *ic, - enum wlr_tablet_tool_type type, enum sway_tablet_tool_mode mode) { - for (int i = 0; i < ic->tools->length; i++) { - struct input_config_tool *tool = ic->tools->items[i]; - if (tool->type == type) { - tool->mode = mode; - return; - } - } - - struct input_config_tool *tool = calloc(1, sizeof(*tool)); - if (tool) { - tool->type = type; - tool->mode = mode; - list_add(ic->tools, tool); - } -} - -struct cmd_results *input_cmd_tool_mode(int argc, char **argv) { - struct cmd_results *error; - if ((error = checkarg(argc, "tool_mode", EXPECTED_AT_LEAST, 2))) { - return error; - } - - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - enum sway_tablet_tool_mode tool_mode; - if (!strcasecmp(argv[1], "absolute")) { - tool_mode = SWAY_TABLET_TOOL_MODE_ABSOLUTE; - } else if (!strcasecmp(argv[1], "relative")) { - tool_mode = SWAY_TABLET_TOOL_MODE_RELATIVE; - } else { - goto invalid_command; - } - - if (!strcasecmp(argv[0], "*")) { - set_tool_mode(ic, WLR_TABLET_TOOL_TYPE_PEN, tool_mode); - set_tool_mode(ic, WLR_TABLET_TOOL_TYPE_ERASER, tool_mode); - set_tool_mode(ic, WLR_TABLET_TOOL_TYPE_BRUSH, tool_mode); - set_tool_mode(ic, WLR_TABLET_TOOL_TYPE_PENCIL, tool_mode); - set_tool_mode(ic, WLR_TABLET_TOOL_TYPE_AIRBRUSH, tool_mode); - } else { - enum wlr_tablet_tool_type tool_type; - if (!strcasecmp(argv[0], "pen")) { - tool_type = WLR_TABLET_TOOL_TYPE_PEN; - } else if (!strcasecmp(argv[0], "eraser")) { - tool_type = WLR_TABLET_TOOL_TYPE_ERASER; - } else if (!strcasecmp(argv[0], "brush")) { - tool_type = WLR_TABLET_TOOL_TYPE_BRUSH; - } else if (!strcasecmp(argv[0], "pencil")) { - tool_type = WLR_TABLET_TOOL_TYPE_PENCIL; - } else if (!strcasecmp(argv[0], "airbrush")) { - tool_type = WLR_TABLET_TOOL_TYPE_AIRBRUSH; - } else { - goto invalid_command; - } - - set_tool_mode(ic, tool_type, tool_mode); - } - - return cmd_results_new(CMD_SUCCESS, NULL); - -invalid_command: - return cmd_results_new(CMD_INVALID, - "Expected 'tool_mode '"); -} diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c deleted file mode 100644 index 056f00e52..000000000 --- a/sway/commands/input/xkb_file.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "log.h" -#include "stringop.h" - -struct cmd_results *input_cmd_xkb_file(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "xkb_file", EXPECTED_EQUAL_TO, 1))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - if (strcmp(argv[0], "-") == 0) { - free(ic->xkb_file); - ic->xkb_file = NULL; - } else { - ic->xkb_file = strdup(argv[0]); - if (!expand_path(&ic->xkb_file)) { - error = cmd_results_new(CMD_INVALID, "Invalid syntax (%s)", - ic->xkb_file); - free(ic->xkb_file); - ic->xkb_file = NULL; - return error; - } - if (!ic->xkb_file) { - sway_log(SWAY_ERROR, "Failed to allocate expanded path"); - return cmd_results_new(CMD_FAILURE, "Unable to allocate resource"); - } - - bool can_access = access(ic->xkb_file, F_OK) != -1; - if (!can_access) { - sway_log_errno(SWAY_ERROR, "Unable to access xkb file '%s'", - ic->xkb_file); - config_add_swaynag_warning("Unable to access xkb file '%s'", - ic->xkb_file); - } - } - ic->xkb_file_is_set = true; - - sway_log(SWAY_DEBUG, "set-xkb_file for config: %s file: %s", - ic->identifier, ic->xkb_file); - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 1d01886c3..0879e2da0 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c @@ -1,5 +1,7 @@ +#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" +#include "sway/input/input-manager.h" #include "log.h" struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) { diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index a9144a8a7..9e3841563 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c @@ -1,5 +1,7 @@ +#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" +#include "sway/input/input-manager.h" #include "log.h" struct cmd_results *input_cmd_xkb_model(int argc, char **argv) { diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c index bbe848fe1..d5b756725 100644 --- a/sway/commands/input/xkb_numlock.c +++ b/sway/commands/input/xkb_numlock.c @@ -1,5 +1,8 @@ +#include +#include #include "sway/config.h" #include "sway/commands.h" +#include "sway/input/input-manager.h" #include "util.h" struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) { diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index 7ca20777b..8197dd1d6 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c @@ -1,5 +1,7 @@ +#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" +#include "sway/input/input-manager.h" #include "log.h" struct cmd_results *input_cmd_xkb_options(int argc, char **argv) { diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 8fbd26fbe..01ccd06f7 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c @@ -1,5 +1,7 @@ +#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" +#include "sway/input/input-manager.h" #include "log.h" struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) { diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c deleted file mode 100644 index 623dfa186..000000000 --- a/sway/commands/input/xkb_switch_layout.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "sway/input/input-manager.h" -#include "sway/server.h" -#include "log.h" - -struct xkb_switch_layout_action { - struct wlr_keyboard *keyboard; - xkb_layout_index_t layout; -}; - -static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { - xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); - if (idx >= num_layouts) { - return; - } - wlr_keyboard_notify_modifiers(kbd, kbd->modifiers.depressed, - kbd->modifiers.latched, kbd->modifiers.locked, idx); -} - -static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) { - xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); - assert(num_layouts > 0); - - xkb_layout_index_t layout_idx; - for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) { - if (xkb_state_layout_index_is_active(kbd->xkb_state, - layout_idx, XKB_STATE_LAYOUT_EFFECTIVE)) { - break; - } - } - return layout_idx; -} - -static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) { - xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); - xkb_layout_index_t idx = get_current_layout_index(kbd); - return (idx + num_layouts + dir) % num_layouts; -} - -struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "xkb_switch_layout", EXPECTED_EQUAL_TO, 1))) { - return error; - } - struct input_config *ic = config->handler_context.input_config; - if (!ic) { - return cmd_results_new(CMD_FAILURE, "No input device defined."); - } - - if (config->reading || !config->active) { - return cmd_results_new(CMD_DEFER, NULL); - } - - const char *layout_str = argv[0]; - int relative, layout; - - if (strcmp(layout_str, "next") == 0) { - relative = 1; - } else if (strcmp(layout_str, "prev") == 0) { - relative = -1; - } else { - char *end; - layout = strtol(layout_str, &end, 10); - if (layout_str[0] == '\0' || end[0] != '\0') { - return cmd_results_new(CMD_FAILURE, "Invalid argument."); - } else if (layout < 0) { - return cmd_results_new(CMD_FAILURE, "Invalid layout index."); - } - relative = 0; - } - - struct xkb_switch_layout_action *actions = calloc( - wl_list_length(&server.input->devices), - sizeof(struct xkb_switch_layout_action)); - size_t actions_len = 0; - - if (!actions) { - return cmd_results_new(CMD_FAILURE, "Unable to allocate actions"); - } - - /* Calculate new indexes first because switching a layout in one - keyboard may result in a change on other keyboards as well because - of keyboard groups. */ - struct sway_input_device *dev; - wl_list_for_each(dev, &server.input->devices, link) { - if (strcmp(ic->identifier, "*") != 0 && - strcmp(ic->identifier, "type:keyboard") != 0 && - strcmp(ic->identifier, dev->identifier) != 0) { - continue; - } - if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { - continue; - } - - struct wlr_keyboard *keyboard = - wlr_keyboard_from_input_device(dev->wlr_device); - if (keyboard->keymap == NULL && dev->is_virtual) { - // The `sway_keyboard_set_layout` function is by default skipped - // when configuring virtual keyboards. - continue; - } - - struct xkb_switch_layout_action *action = - &actions[actions_len++]; - action->keyboard = keyboard; - - if (relative) { - action->layout = get_layout_relative(action->keyboard, relative); - } else { - action->layout = layout; - } - } - - for (size_t i = 0; i < actions_len; i++) { - switch_layout(actions[i].keyboard, actions[i].layout); - } - free(actions); - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index 2d14ea9c7..37c82fbd6 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c @@ -1,5 +1,7 @@ +#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" +#include "sway/input/input-manager.h" #include "log.h" struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) { diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 22dfdf3d7..2aca31dc5 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -2,7 +2,6 @@ #include #include #include "sway/commands.h" -#include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" @@ -26,37 +25,18 @@ static const char expected_syntax[] = "'layout toggle [split|all]' or " "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; -static enum sway_container_layout toggle_split_layout( - enum sway_container_layout layout, - enum sway_container_layout prev_split_layout, - struct sway_output *output) { - if (layout == L_HORIZ) { - return L_VERT; - } else if (layout == L_VERT) { - return L_HORIZ; - } else if (prev_split_layout != L_NONE) { - return prev_split_layout; - } else if (config->default_orientation != L_NONE) { - return config->default_orientation; - } else if (output->height > output->width) { - return L_VERT; - } - return L_HORIZ; -} - static enum sway_container_layout get_layout_toggle(int argc, char **argv, enum sway_container_layout layout, - enum sway_container_layout prev_split_layout, - struct sway_output *output) { + enum sway_container_layout prev_split_layout) { // "layout toggle" if (argc == 1) { - return toggle_split_layout(layout, prev_split_layout, output); + return layout == L_HORIZ ? L_VERT : L_HORIZ; } if (argc == 2) { // "layout toggle split" (same as "layout toggle") if (strcasecmp(argv[1], "split") == 0) { - return toggle_split_layout(layout, prev_split_layout, output); + return layout == L_HORIZ ? L_VERT : L_HORIZ; } // "layout toggle all" if (strcasecmp(argv[1], "all") == 0) { @@ -86,7 +66,8 @@ static enum sway_container_layout get_layout_toggle(int argc, char **argv, return parsed; } if (strcmp(argv[i], "split") == 0) { - return toggle_split_layout(layout, prev_split_layout, output); + return layout == L_HORIZ ? L_VERT : + layout == L_VERT ? L_HORIZ : prev_split_layout; } // invalid layout strings are silently ignored } @@ -95,8 +76,7 @@ static enum sway_container_layout get_layout_toggle(int argc, char **argv, static enum sway_container_layout get_layout(int argc, char **argv, enum sway_container_layout layout, - enum sway_container_layout prev_split_layout, - struct sway_output *output) { + enum sway_container_layout prev_split_layout) { // Check if assigned directly enum sway_container_layout parsed = parse_layout_string(argv[0]); if (parsed != L_NONE) { @@ -108,7 +88,7 @@ static enum sway_container_layout get_layout(int argc, char **argv, } if (strcasecmp(argv[0], "toggle") == 0) { - return get_layout_toggle(argc, argv, layout, prev_split_layout, output); + return get_layout_toggle(argc, argv, layout, prev_split_layout); } return L_NONE; @@ -131,22 +111,11 @@ struct cmd_results *cmd_layout(int argc, char **argv) { "Unable to change layout of floating windows"); } - // Operate on parent container, like i3. - if (container) { - container = container->pending.parent; - // If parent has only a singe child operate on its parent and - // flatten once, like i3 - if (container && container->pending.children->length == 1) { - // Also check grandparent to avoid restricting layouts - struct sway_container *parent = container->pending.parent; - if (parent && parent->pending.children->length == 1) { - struct sway_container *child = container->pending.children->items[0]; - struct sway_container *parent = container->pending.parent; - container_replace(container, child); - container_begin_destroy(container); - container = parent; - } - } + // Typically we change the layout of the current container, but if the + // current container is a view (it usually is) then we'll change the layout + // of the parent instead, as it doesn't make sense for views to have layout. + if (container && container->view) { + container = container->parent; } // We could be working with a container OR a workspace. These are different @@ -155,31 +124,23 @@ struct cmd_results *cmd_layout(int argc, char **argv) { enum sway_container_layout new_layout = L_NONE; enum sway_container_layout old_layout = L_NONE; if (container) { - old_layout = container->pending.layout; + old_layout = container->layout; new_layout = get_layout(argc, argv, - container->pending.layout, container->prev_split_layout, - container->pending.workspace->output); + container->layout, container->prev_split_layout); } else { old_layout = workspace->layout; new_layout = get_layout(argc, argv, - workspace->layout, workspace->prev_split_layout, - workspace->output); + workspace->layout, workspace->prev_split_layout); } if (new_layout == L_NONE) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } if (new_layout != old_layout) { if (container) { if (old_layout != L_TABBED && old_layout != L_STACKED) { container->prev_split_layout = old_layout; } - container->pending.layout = new_layout; - container_update_representation(container); - } else if (config->handler_context.container) { - // i3 avoids changing workspace layouts with a new container - // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817 - container = workspace_wrap_children(workspace); - container->pending.layout = new_layout; + container->layout = new_layout; container_update_representation(container); } else { if (old_layout != L_TABBED && old_layout != L_STACKED) { @@ -188,11 +149,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { workspace->layout = new_layout; workspace_update_representation(workspace); } - if (root->fullscreen_global) { - arrange_root(); - } else { - arrange_workspace(workspace); - } + arrange_workspace(workspace); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 77c8d2394..aa5f185c8 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -23,7 +24,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } bool add = false, toggle = false; - while (argc > 0 && has_prefix(*argv, "--")) { + while (argc > 0 && strncmp(*argv, "--", 2) == 0) { if (strcmp(*argv, "--add") == 0) { add = true; } else if (strcmp(*argv, "--replace") == 0) { @@ -58,7 +59,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } free(mark); - container_update_marks(container); + container_update_marks_textures(container); if (container->view) { view_execute_criteria(container->view); } diff --git a/sway/commands/max_render_time.c b/sway/commands/max_render_time.c deleted file mode 100644 index ec3282f18..000000000 --- a/sway/commands/max_render_time.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/tree/view.h" - -struct cmd_results *cmd_max_render_time(int argc, char **argv) { - if (!argc) { - return cmd_results_new(CMD_INVALID, "Missing max render time argument."); - } - - int max_render_time; - if (!strcmp(*argv, "off")) { - max_render_time = 0; - } else { - char *end; - max_render_time = strtol(*argv, &end, 10); - if (*end || max_render_time <= 0) { - return cmd_results_new(CMD_INVALID, "Invalid max render time."); - } - } - - struct sway_container *container = config->handler_context.container; - if (!container || !container->view) { - return cmd_results_new(CMD_INVALID, - "Only views can have a max_render_time"); - } - - struct sway_view *view = container->view; - view->max_render_time = max_render_time; - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/mode.c b/sway/commands/mode.c index b32169679..d424eb1ce 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -1,5 +1,7 @@ +#define _POSIX_C_SOURCE 200809L #include #include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/ipc-server.h" @@ -8,16 +10,9 @@ #include "stringop.h" // Must be in order for the bsearch -static const struct cmd_handler mode_handlers[] = { +static struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, - { "bindgesture", cmd_bindgesture }, - { "bindswitch", cmd_bindswitch }, - { "bindsym", cmd_bindsym }, - { "set", cmd_set }, - { "unbindcode", cmd_unbindcode }, - { "unbindgesture", cmd_unbindgesture }, - { "unbindswitch", cmd_unbindswitch }, - { "unbindsym", cmd_unbindsym }, + { "bindsym", cmd_bindsym } }; struct cmd_results *cmd_mode(int argc, char **argv) { @@ -26,6 +21,10 @@ struct cmd_results *cmd_mode(int argc, char **argv) { return error; } + if (argc > 1 && !config->reading) { + return cmd_results_new(CMD_FAILURE, "Can only be used in config file"); + } + bool pango = strcmp(*argv, "--pango_markup") == 0; if (pango) { argc--; argv++; @@ -34,17 +33,13 @@ struct cmd_results *cmd_mode(int argc, char **argv) { } } - if (config->reading && argc == 1) { - return cmd_results_new(CMD_DEFER, NULL); - } - char *mode_name = *argv; strip_quotes(mode_name); struct sway_mode *mode = NULL; // Find mode for (int i = 0; i < config->modes->length; ++i) { struct sway_mode *test = config->modes->items[i]; - if (strcmp(test->name, mode_name) == 0) { + if (strcasecmp(test->name, mode_name) == 0) { mode = test; break; } @@ -59,8 +54,6 @@ 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->gesture_bindings = create_list(); mode->pango = pango; list_add(config->modes, mode); } @@ -68,13 +61,14 @@ struct cmd_results *cmd_mode(int argc, char **argv) { error = cmd_results_new(CMD_INVALID, "Unknown mode `%s'", mode_name); return error; } + if ((config->reading && argc > 1) || (!config->reading && argc == 1)) { + sway_log(SWAY_DEBUG, "Switching to mode `%s' (pango=%d)", + mode->name, mode->pango); + } // Set current mode - struct sway_mode *stored_mode = config->current_mode; config->current_mode = mode; if (argc == 1) { // trigger IPC mode event - sway_log(SWAY_DEBUG, "Switching to mode `%s' (pango=%d)", - mode->name, mode->pango); ipc_event_mode(config->current_mode->name, config->current_mode->pango); return cmd_results_new(CMD_SUCCESS, NULL); @@ -83,7 +77,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { // Create binding struct cmd_results *result = config_subcommand(argv + 1, argc - 1, mode_handlers, sizeof(mode_handlers)); - config->current_mode = stored_mode; + config->current_mode = config->modes->items[0]; return result; } diff --git a/sway/commands/move.c b/sway/commands/move.c index 43fce0d6d..16f8cdb6f 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,5 +1,5 @@ +#define _POSIX_C_SOURCE 200809L #include -#include #include #include #include @@ -11,7 +11,6 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" -#include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -29,15 +28,6 @@ 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; @@ -113,41 +103,36 @@ static void container_move_to_container_from_direction( struct sway_container *container, struct sway_container *destination, enum wlr_direction move_dir) { if (destination->view) { - if (destination->pending.parent == container->pending.parent && - destination->pending.workspace == container->pending.workspace) { + if (destination->parent == container->parent && + destination->workspace == container->workspace) { sway_log(SWAY_DEBUG, "Swapping siblings"); list_t *siblings = container_get_siblings(container); int container_index = list_find(siblings, container); int destination_index = list_find(siblings, destination); list_swap(siblings, container_index, destination_index); - container_update_representation(container); } else { sway_log(SWAY_DEBUG, "Promoting to sibling of cousin"); int offset = move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; int index = container_sibling_index(destination) + offset; - if (destination->pending.parent) { - container_insert_child(destination->pending.parent, container, index); + if (destination->parent) { + container_insert_child(destination->parent, container, index); } else { - workspace_insert_tiling(destination->pending.workspace, + workspace_insert_tiling(destination->workspace, container, index); } - container->pending.width = container->pending.height = 0; - container->width_fraction = container->height_fraction = 0; - workspace_squash(destination->pending.workspace); + container->width = container->height = 0; } return; } - if (is_parallel(destination->pending.layout, move_dir)) { + if (is_parallel(destination->layout, move_dir)) { sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); int index = move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? - 0 : destination->pending.children->length; + 0 : destination->children->length; container_insert_child(destination, container, index); - container->pending.width = container->pending.height = 0; - container->width_fraction = container->height_fraction = 0; - workspace_squash(destination->pending.workspace); + container->width = container->height = 0; return; } @@ -168,8 +153,7 @@ static void container_move_to_container_from_direction( static void container_move_to_workspace_from_direction( struct sway_container *container, struct sway_workspace *workspace, enum wlr_direction move_dir) { - container->pending.width = container->pending.height = 0; - container->width_fraction = container->height_fraction = 0; + container->width = container->height = 0; if (is_parallel(workspace->layout, move_dir)) { sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); @@ -188,8 +172,8 @@ static void container_move_to_workspace_from_direction( workspace_add_tiling(workspace, container); return; } - while (focus_inactive->pending.parent) { - focus_inactive = focus_inactive->pending.parent; + while (focus_inactive->parent) { + focus_inactive = focus_inactive->parent; } container_move_to_container_from_direction(container, focus_inactive, move_dir); @@ -197,37 +181,24 @@ static void container_move_to_workspace_from_direction( static void container_move_to_workspace(struct sway_container *container, struct sway_workspace *workspace) { - if (container->pending.workspace == workspace) { + if (container->workspace == workspace) { return; } - struct sway_workspace *old_workspace = container->pending.workspace; + struct sway_workspace *old_workspace = container->workspace; if (container_is_floating(container)) { - struct sway_output *old_output = container->pending.workspace->output; + struct sway_output *old_output = container->workspace->output; container_detach(container); workspace_add_floating(workspace, container); container_handle_fullscreen_reparent(container); - // If changing output, adjust the coordinates of the window. - if (old_output != workspace->output && !container->pending.fullscreen_mode) { - struct wlr_box workspace_box, old_workspace_box; - workspace_get_box(workspace, &workspace_box); - workspace_get_box(old_workspace, &old_workspace_box); - floating_fix_coordinates(container, &old_workspace_box, &workspace_box); - if (container->scratchpad && workspace->output) { - struct wlr_box output_box; - output_get_box(workspace->output, &output_box); - container->transform = workspace_box; - } + // If changing output, center it within the workspace + if (old_output != workspace->output && !container->fullscreen_mode) { + container_floating_move_to_center(container); } } else { container_detach(container); - if (workspace_is_empty(workspace) && container->pending.children) { - workspace_unwrap_children(workspace, container); - container_reap_empty(container); - } else { - container->pending.width = container->pending.height = 0; - container->width_fraction = container->height_fraction = 0; - workspace_add_tiling(workspace, container); - } + container->width = container->height = 0; + container->saved_width = container->saved_height = 0; + workspace_add_tiling(workspace, container); container_update_representation(container); } if (container->view) { @@ -241,18 +212,20 @@ static void container_move_to_workspace(struct sway_container *container, static void container_move_to_container(struct sway_container *container, struct sway_container *destination) { if (container == destination + || container_has_ancestor(container, destination) || container_has_ancestor(destination, container)) { return; } if (container_is_floating(container)) { - container_move_to_workspace(container, destination->pending.workspace); + container_move_to_workspace(container, destination->workspace); return; } - struct sway_workspace *old_workspace = container->pending.workspace; + struct sway_workspace *old_workspace = container->workspace; container_detach(container); - container->pending.width = container->pending.height = 0; - container->width_fraction = container->height_fraction = 0; + container_remove_gaps(container); + container->width = container->height = 0; + container->saved_width = container->saved_height = 0; if (destination->view) { container_add_sibling(destination, container, 1); @@ -264,163 +237,140 @@ static void container_move_to_container(struct sway_container *container, ipc_event_window(container, "move"); } - if (destination->pending.workspace) { - workspace_focus_fullscreen(destination->pending.workspace); - workspace_detect_urgent(destination->pending.workspace); - } + workspace_focus_fullscreen(destination->workspace); - if (old_workspace && old_workspace != destination->pending.workspace) { + // Update workspace urgent state + workspace_detect_urgent(destination->workspace); + if (old_workspace && old_workspace != destination->workspace) { workspace_detect_urgent(old_workspace); } } -static bool container_move_to_next_output(struct sway_container *container, - struct sway_output *output, enum wlr_direction move_dir) { - struct sway_output *next_output = - output_get_in_direction(output, move_dir); - if (next_output) { - struct sway_workspace *ws = output_get_active_workspace(next_output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return false; - } - switch (container->pending.fullscreen_mode) { - case FULLSCREEN_NONE: - container_move_to_workspace_from_direction(container, ws, move_dir); - return true; - case FULLSCREEN_WORKSPACE: - container_move_to_workspace(container, ws); - return true; - case FULLSCREEN_GLOBAL: - return false; - } +/* Takes one child, sets it aside, wraps the rest of the children in a new + * container, switches the layout of the workspace, and drops the child back in. + * In other words, rejigger it. */ +static void workspace_rejigger(struct sway_workspace *ws, + struct sway_container *child, enum wlr_direction move_dir) { + if (!child->parent && ws->tiling->length == 1) { + ws->layout = + move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ? + L_HORIZ : L_VERT; + workspace_update_representation(ws); + return; } - return false; + container_detach(child); + struct sway_container *new_parent = workspace_wrap_children(ws); + + int index = + move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? 0 : 1; + workspace_insert_tiling(ws, child, index); + container_flatten(new_parent); + ws->layout = + move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ? + L_HORIZ : L_VERT; + workspace_update_representation(ws); + child->width = child->height = 0; } // Returns true if moved static bool container_move_in_direction(struct sway_container *container, enum wlr_direction move_dir) { // If moving a fullscreen view, only consider outputs - switch (container->pending.fullscreen_mode) { - case FULLSCREEN_NONE: - break; - case FULLSCREEN_WORKSPACE: - return container_move_to_next_output(container, - container->pending.workspace->output, move_dir); - case FULLSCREEN_GLOBAL: + if (container->fullscreen_mode == FULLSCREEN_WORKSPACE) { + struct sway_output *new_output = + output_get_in_direction(container->workspace->output, move_dir); + if (!new_output) { + return false; + } + struct sway_workspace *ws = output_get_active_workspace(new_output); + container_move_to_workspace(container, ws); + return true; + } + if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { return false; } + // If container is in a split container by itself, move out of the split + if (container->parent) { + struct sway_container *new_parent = + container_flatten(container->parent); + if (new_parent != container->parent) { + return true; + } + } + + // Look for a suitable *container* sibling or parent. + // The below loop stops once we hit the workspace because current->parent + // is NULL for the topmost containers in a workspace. + struct sway_container *current = container; int offs = move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? -1 : 1; - int index = -1; - int desired = -1; - list_t *siblings = NULL; - struct sway_container *target = NULL; - // Look for a suitable ancestor of the container to move within - struct sway_container *ancestor = NULL; - struct sway_container *current = container; - bool wrapped = false; - while (!ancestor) { + 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; + // Don't allow containers to move out of their // fullscreen or floating parent - if (current->pending.fullscreen_mode || container_is_floating(current)) { + if (current->fullscreen_mode || container_is_floating(current)) { return false; } - enum sway_container_layout parent_layout = container_parent_layout(current); - if (!is_parallel(parent_layout, move_dir)) { - if (!current->pending.parent) { - // No parallel parent, so we reorient the workspace - current = workspace_wrap_children(current->pending.workspace); - current->pending.workspace->layout = - move_dir == WLR_DIRECTION_LEFT || - move_dir == WLR_DIRECTION_RIGHT ? - L_HORIZ : L_VERT; - container->pending.height = container->pending.width = 0; - container->height_fraction = container->width_fraction = 0; - workspace_update_representation(current->pending.workspace); - wrapped = true; + 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)); + } else { + workspace_insert_tiling(current->workspace, container, + index + (offs < 0 ? 0 : 1)); + } + return true; + } } else { - // Keep looking for a parallel parent - current = current->pending.parent; - } - continue; - } - - // Only scratchpad hidden containers don't have siblings - // so siblings != NULL here - siblings = container_get_siblings(current); - index = list_find(siblings, current); - desired = index + offs; - target = desired == -1 || desired == siblings->length ? - NULL : siblings->items[desired]; - - // If the move is simple we can complete it here early - if (current == container) { - if (target) { - // Container will swap with or descend into its neighbor + // Container can move within its siblings container_move_to_container_from_direction(container, - target, move_dir); + siblings->items[desired], move_dir); return true; - } else if (!container->pending.parent) { - // Container is at workspace level so we move it to the - // next workspace if possible - return container_move_to_next_output(container, - current->pending.workspace->output, move_dir); - } else { - // Container has escaped its immediate parallel parent - current = current->pending.parent; - continue; } } - // We found a suitable ancestor, the loop will end - ancestor = current; + current = current->parent; } - if (target) { - // Container will move in with its cousin - container_move_to_container_from_direction(container, - target, move_dir); + // 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 (!wrapped && !container->pending.parent->pending.parent && - container->pending.parent->pending.children->length == 1) { - // Treat singleton children as if they are at workspace level like i3 - // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 - return container_move_to_next_output(container, - ancestor->pending.workspace->output, move_dir); - } else { - // Container will be promoted - struct sway_container *old_parent = container->pending.parent; - if (ancestor->pending.parent) { - // Container will move in with its parent - container_insert_child(ancestor->pending.parent, container, - index + (offs < 0 ? 0 : 1)); - } else { - // Container will move to workspace level, - // may be re-split by workspace_layout - workspace_insert_tiling(ancestor->pending.workspace, container, - index + (offs < 0 ? 0 : 1)); - } - ancestor->pending.height = ancestor->pending.width = 0; - ancestor->height_fraction = ancestor->width_fraction = 0; - if (old_parent) { - container_reap_empty(old_parent); - } - workspace_squash(container->pending.workspace); + } 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); + container_move_to_workspace_from_direction(container, ws, move_dir); + return true; + } + sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); + return false; } -static struct cmd_results *cmd_move_to_scratchpad(void); - -static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, - int argc, char **argv) { +static struct cmd_results *cmd_move_container(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move container/window", - EXPECTED_AT_LEAST, 2))) { + EXPECTED_AT_LEAST, 3))) { return error; } @@ -435,30 +385,48 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, container = workspace_wrap_children(workspace); } - if (container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { - return cmd_results_new(CMD_FAILURE, - "Can't move fullscreen global container"); + 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++; } struct sway_seat *seat = config->handler_context.seat; - struct sway_container *old_parent = container->pending.parent; - struct sway_workspace *old_ws = container->pending.workspace; + struct sway_container *old_parent = container->parent; + struct sway_workspace *old_ws = container->workspace; struct sway_output *old_output = old_ws ? old_ws->output : NULL; struct sway_node *destination = NULL; // determine destination - if (strcasecmp(argv[0], "workspace") == 0) { + if (strcasecmp(argv[1], "workspace") == 0) { // move container to workspace x struct sway_workspace *ws = NULL; char *ws_name = NULL; - 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 (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 (seat->prev_workspace_name) { ws_name = strdup(seat->prev_workspace_name); } else { @@ -467,19 +435,19 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } } } else { - if (strcasecmp(argv[1], "number") == 0) { - // move [window|container] [to] "workspace number x" - if (argc < 3) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + if (strcasecmp(argv[2], "number") == 0) { + // move "container to workspace number x" + if (argc < 4) { + return cmd_results_new(CMD_INVALID, expected_syntax); } - if (!isdigit(argv[2][0])) { + if (!isdigit(argv[3][0])) { return cmd_results_new(CMD_INVALID, - "Invalid workspace number '%s'", argv[2]); + "Invalid workspace number '%s'", argv[3]); } - ws_name = join_args(argv + 2, argc - 2); + ws_name = join_args(argv + 3, argc - 3); ws = workspace_by_number(ws_name); } else { - ws_name = join_args(argv + 1, argc - 1); + ws_name = join_args(argv + 2, argc - 2); ws = workspace_by_name(ws_name); } @@ -499,7 +467,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, // 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_or_child(container)) { + if (container->is_sticky) { struct sway_output *new_output = workspace_get_initial_output(ws_name); if (old_output == new_output) { @@ -510,53 +478,44 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } } ws = workspace_create(NULL, ws_name); - arrange_workspace(ws); } free(ws_name); struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); destination = dst ? &dst->node : &ws->node; - } else if (strcasecmp(argv[0], "output") == 0) { - struct sway_output *new_output = output_in_direction(argv[1], - old_output, container->pending.x, container->pending.y); + } else if (strcasecmp(argv[1], "output") == 0) { + struct sway_output *new_output = output_in_direction(argv[2], + old_output, container->x, container->y); if (!new_output) { return cmd_results_new(CMD_FAILURE, - "Can't find output with name/direction '%s'", argv[1]); + "Can't find output with name/direction '%s'", argv[2]); } destination = seat_get_focus_inactive(seat, &new_output->node); - } else if (strcasecmp(argv[0], "mark") == 0) { - struct sway_container *dest_con = container_find_mark(argv[1]); + } else if (strcasecmp(argv[1], "mark") == 0) { + struct sway_container *dest_con = container_find_mark(argv[2]); if (dest_con == NULL) { return cmd_results_new(CMD_FAILURE, - "Mark '%s' not found", argv[1]); + "Mark '%s' not found", argv[2]); } destination = &dest_con->node; } else { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } - if (destination->type == N_CONTAINER && - container_is_scratchpad_hidden(destination->sway_container)) { - return cmd_move_to_scratchpad(); - } - - if (container_is_sticky_or_child(container) && old_output && + if (container->is_sticky && 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"); } struct sway_output *new_output = node_get_output(destination); - struct sway_workspace *new_output_last_ws = NULL; - if (new_output && old_output != new_output) { - new_output_last_ws = output_get_active_workspace(new_output); - } + struct sway_workspace *new_output_last_ws = old_output == new_output ? + NULL : output_get_active_workspace(new_output); // save focus, in case it needs to be restored struct sway_node *focus = seat_get_focus(seat); // move container - if (container_is_scratchpad_hidden_or_child(container)) { - container_detach(container); + if (container->scratchpad) { root_scratchpad_show(container); } switch (destination->type) { @@ -566,10 +525,6 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, case N_OUTPUT: { struct sway_output *output = destination->sway_output; struct sway_workspace *ws = output_get_active_workspace(output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return cmd_results_new(CMD_FAILURE, - "Expected output to have a workspace"); - } container_move_to_workspace(container, ws); } break; @@ -581,14 +536,8 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } // restore focus on destination output back to its last active workspace - struct sway_workspace *new_workspace = new_output ? - output_get_active_workspace(new_output) : NULL; - if (new_output && - !sway_assert(new_workspace, "Expected output to have a workspace")) { - return cmd_results_new(CMD_FAILURE, - "Expected output to have a workspace"); - } - + struct sway_workspace *new_workspace = + output_get_active_workspace(new_output); if (new_output_last_ws && new_output_last_ws != new_workspace) { struct sway_node *new_output_last_focus = seat_get_focus_inactive(seat, &new_output_last_ws->node); @@ -627,54 +576,78 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, return cmd_results_new(CMD_SUCCESS, NULL); } +static void workspace_move_to_output(struct sway_workspace *workspace, + struct sway_output *output) { + if (workspace->output == output) { + return; + } + struct sway_output *old_output = workspace->output; + workspace_detach(workspace); + struct sway_workspace *new_output_old_ws = + output_get_active_workspace(output); + + output_add_workspace(output, workspace); + + // If moving the last workspace from the old output, create a new workspace + // on the old output + struct sway_seat *seat = config->handler_context.seat; + if (old_output->workspaces->length == 0) { + char *ws_name = workspace_next_name(old_output->wlr_output->name); + struct sway_workspace *ws = workspace_create(old_output, ws_name); + free(ws_name); + seat_set_raw_focus(seat, &ws->node); + } + + workspace_consider_destroy(new_output_old_ws); + + output_sort_workspaces(output); + struct sway_node *focus = seat_get_focus_inactive(seat, &workspace->node); + seat_set_focus(seat, focus); + workspace_output_raise_priority(workspace, old_output, output); + ipc_event_workspace(NULL, workspace, "move"); +} + 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, 1))) { + if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { return error; } - if (strcasecmp(argv[0], "output") == 0) { - --argc; ++argv; + while (strcasecmp(argv[1], "to") == 0) { + if (--argc < 3) { + return cmd_results_new(CMD_INVALID, expected_syntax); + } + ++argv; } - if (!argc) { - return cmd_results_new(CMD_INVALID, - "Expected 'move workspace to [output] '"); + if (strcasecmp(argv[1], "output") != 0) { + return cmd_results_new(CMD_INVALID, expected_syntax); } 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[0], + struct sway_output *new_output = output_in_direction(argv[2], old_output, center_x, center_y); if (!new_output) { return cmd_results_new(CMD_FAILURE, - "Can't find output with name/direction '%s'", argv[0]); + "Can't find output with name/direction '%s'", argv[2]); } workspace_move_to_output(workspace, new_output); arrange_output(old_output); arrange_output(new_output); - struct sway_seat *seat = config->handler_context.seat; - struct sway_node *focus = seat_get_focus_inactive(seat, &workspace->node); - seat_set_focus(seat, focus); - seat_consider_warp_to_focus(seat); - return cmd_results_new(CMD_SUCCESS, NULL); } static struct cmd_results *cmd_move_in_direction( enum wlr_direction direction, int argc, char **argv) { int move_amt = 10; - if (argc) { + if (argc > 1) { char *inv; - move_amt = (int)strtol(argv[0], &inv, 10); + move_amt = (int)strtol(argv[1], &inv, 10); if (*inv != '\0' && strcasecmp(inv, "px") != 0) { return cmd_results_new(CMD_FAILURE, "Invalid distance specified"); } @@ -685,13 +658,17 @@ static struct cmd_results *cmd_move_in_direction( return cmd_results_new(CMD_FAILURE, "Cannot move workspaces in a direction"); } + if (container_is_scratchpad_hidden(container)) { + return cmd_results_new(CMD_FAILURE, + "Cannot move a hidden scratchpad container"); + } if (container_is_floating(container)) { - if (container->pending.fullscreen_mode) { + if (container->fullscreen_mode) { return cmd_results_new(CMD_FAILURE, "Cannot move fullscreen floating container"); } - double lx = container->pending.x; - double ly = container->pending.y; + double lx = container->x; + double ly = container->y; switch (direction) { case WLR_DIRECTION_LEFT: lx -= move_amt; @@ -709,22 +686,14 @@ static struct cmd_results *cmd_move_in_direction( container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL); } - struct sway_workspace *old_ws = container->pending.workspace; - struct sway_container *old_parent = container->pending.parent; + struct sway_workspace *old_ws = container->workspace; if (!container_move_in_direction(container, direction)) { // Container didn't move return cmd_results_new(CMD_SUCCESS, NULL); } - // clean-up, destroying parents if the container was the last child - if (old_parent) { - container_reap_empty(old_parent); - } else if (old_ws) { - workspace_consider_destroy(old_ws); - } - - struct sway_workspace *new_ws = container->pending.workspace; + struct sway_workspace *new_ws = container->workspace; if (root->fullscreen_global) { arrange_root(); @@ -739,43 +708,20 @@ static struct cmd_results *cmd_move_in_direction( ipc_event_window(container, "move"); } + // Hack to re-focus container + seat_set_raw_focus(config->handler_context.seat, &new_ws->node); + seat_set_focus_container(config->handler_context.seat, container); + + if (old_ws != new_ws) { + ipc_event_workspace(old_ws, new_ws, "focus"); + workspace_detect_urgent(old_ws); + workspace_detect_urgent(new_ws); + } container_end_mouse_operation(container); return cmd_results_new(CMD_SUCCESS, NULL); } -static struct cmd_results *cmd_move_to_position_pointer( - struct sway_container *container) { - struct sway_seat *seat = config->handler_context.seat; - if (!seat->cursor) { - return cmd_results_new(CMD_FAILURE, "No cursor device"); - } - struct wlr_cursor *cursor = seat->cursor->cursor; - /* Determine where to put the window. */ - double lx = cursor->x - container->pending.width / 2; - double ly = cursor->y - container->pending.height / 2; - - /* Correct target coordinates to be in bounds (on screen). */ - struct wlr_output *output = wlr_output_layout_output_at( - root->output_layout, cursor->x, cursor->y); - if (output) { - struct wlr_box box; - wlr_output_layout_get_box(root->output_layout, output, &box); - lx = fmax(lx, box.x); - ly = fmax(ly, box.y); - if (lx + container->pending.width > box.x + box.width) { - lx = box.x + box.width - container->pending.width; - } - if (ly + container->pending.height > box.y + box.height) { - ly = box.y + box.height - container->pending.height; - } - } - - /* Actually move the container. */ - container_floating_move_to(container, lx, ly); - return cmd_results_new(CMD_SUCCESS, NULL); -} - static const char expected_position_syntax[] = "Expected 'move [absolute] position [px] [px]' or " "'move [absolute] position center' or " @@ -787,9 +733,13 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Only floating containers " "can be moved to an absolute position"); } + if (container_is_scratchpad_hidden(container)) { + return cmd_results_new(CMD_FAILURE, + "Cannot move a hidden scratchpad container"); + } if (!argc) { - return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); + return cmd_results_new(CMD_FAILURE, expected_position_syntax); } bool absolute = false; @@ -799,122 +749,69 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { ++argv; } if (!argc) { - return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); + return cmd_results_new(CMD_FAILURE, expected_position_syntax); } if (strcmp(argv[0], "position") == 0) { --argc; ++argv; } if (!argc) { - return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); + return cmd_results_new(CMD_FAILURE, 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, "%s", expected_position_syntax); + struct sway_seat *seat = config->handler_context.seat; + if (!seat->cursor) { + return cmd_results_new(CMD_FAILURE, "No cursor device"); } - return cmd_move_to_position_pointer(container); + double lx = seat->cursor->cursor->x - container->width / 2; + double ly = seat->cursor->cursor->y - container->height / 2; + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL); } else if (strcmp(argv[0], "center") == 0) { double lx, ly; if (absolute) { - lx = root->x + (root->width - container->pending.width) / 2; - ly = root->y + (root->height - container->pending.height) / 2; + lx = root->x + (root->width - container->width) / 2; + ly = root->y + (root->height - container->height) / 2; } else { - struct sway_workspace *ws = container->pending.workspace; - if (!ws) { - struct sway_seat *seat = config->handler_context.seat; - ws = seat_get_focused_workspace(seat); - } - lx = ws->x + (ws->width - container->pending.width) / 2; - ly = ws->y + (ws->height - container->pending.height) / 2; + struct sway_workspace *ws = container->workspace; + lx = ws->x + (ws->width - container->width) / 2; + ly = ws->y + (ws->height - container->height) / 2; } container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL); } if (argc < 2) { - return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); + return cmd_results_new(CMD_FAILURE, expected_position_syntax); } - struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; - // X direction - int num_consumed_args = parse_movement_amount(argc, argv, &lx); - argc -= num_consumed_args; - argv += num_consumed_args; - if (lx.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "Invalid x position specified"); + double lx, ly; + char *inv; + lx = (double)strtol(argv[0], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "Invalid position specified"); + } + if (strcmp(argv[1], "px") == 0) { + --argc; + ++argv; } - if (argc < 1) { - return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); + if (argc > 3) { + return cmd_results_new(CMD_FAILURE, expected_position_syntax); } - struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; - // Y direction - num_consumed_args = parse_movement_amount(argc, argv, &ly); - argc -= num_consumed_args; - argv += num_consumed_args; - if (argc > 0) { - return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); - } - if (ly.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "Invalid y position specified"); + ly = (double)strtol(argv[1], &inv, 10); + if ((*inv != '\0' && strcasecmp(inv, "px") != 0) || + (argc == 3 && strcmp(argv[2], "px") != 0)) { + return cmd_results_new(CMD_FAILURE, "Invalid position specified"); } - struct sway_workspace *ws = container->pending.workspace; - if (!ws) { - struct sway_seat *seat = config->handler_context.seat; - ws = seat_get_focused_workspace(seat); - } - - switch (lx.unit) { - case MOVEMENT_UNIT_PPT: - if (container_is_scratchpad_hidden(container)) { - return cmd_results_new(CMD_FAILURE, - "Cannot move a hidden scratchpad container by ppt"); - } - if (absolute) { - return cmd_results_new(CMD_FAILURE, - "Cannot move to absolute positions by ppt"); - } - // Convert to px - lx.amount = ws->width * lx.amount / 100; - lx.unit = MOVEMENT_UNIT_PX; - // Falls through - case MOVEMENT_UNIT_PX: - case MOVEMENT_UNIT_DEFAULT: - break; - case MOVEMENT_UNIT_INVALID: - sway_assert(false, "invalid x unit"); - break; - } - - switch (ly.unit) { - case MOVEMENT_UNIT_PPT: - if (container_is_scratchpad_hidden(container)) { - return cmd_results_new(CMD_FAILURE, - "Cannot move a hidden scratchpad container by ppt"); - } - if (absolute) { - return cmd_results_new(CMD_FAILURE, - "Cannot move to absolute positions by ppt"); - } - // Convert to px - ly.amount = ws->height * ly.amount / 100; - ly.unit = MOVEMENT_UNIT_PX; - // Falls through - case MOVEMENT_UNIT_PX: - case MOVEMENT_UNIT_DEFAULT: - break; - case MOVEMENT_UNIT_INVALID: - sway_assert(false, "invalid y unit"); - break; - } if (!absolute) { - lx.amount += ws->x; - ly.amount += ws->y; + lx += container->workspace->x; + ly += container->workspace->y; } - container_floating_move_to(container, lx.amount, ly.amount); + container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL); } @@ -935,31 +832,19 @@ static struct cmd_results *cmd_move_to_scratchpad(void) { // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(con)) { - while (con->pending.parent) { - con = con->pending.parent; + while (con->parent) { + con = con->parent; } } - if (!con->scratchpad) { - root_scratchpad_add_container(con, NULL); - } else if (con->pending.workspace) { - root_scratchpad_hide(con); + if (con->scratchpad) { + return cmd_results_new(CMD_INVALID, + "Container is already in the scratchpad"); } + root_scratchpad_add_container(con); 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))) { @@ -971,55 +856,31 @@ 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], "workspace") == 0 && argc >= 2 - && (strcasecmp(argv[1], "to") == 0 || - strcasecmp(argv[1], "output") == 0)) { - argc -= 2; argv += 2; + 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_workspace(argc, argv); - } - - 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 (argc > 0 && (strcasecmp(argv[0], "window") == 0 || - strcasecmp(argv[0], "container") == 0)) { - --argc; ++argv; - } - - if (argc > 0 && strcasecmp(argv[0], "to") == 0) { - --argc; ++argv; - } - - if (!argc) { - return cmd_results_new(CMD_INVALID, "%s", 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, "%s", 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) { + } 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 || - (argc > 1 && strcasecmp(argv[0], "absolute") == 0 && - strcasecmp(argv[1], "position") == 0)) { + } 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_INVALID, "%s", expected_full_syntax); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/new_float.c b/sway/commands/new_float.c deleted file mode 100644 index 4fedb4cb8..000000000 --- a/sway/commands/new_float.c +++ /dev/null @@ -1,13 +0,0 @@ -#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 deleted file mode 100644 index e8caa4873..000000000 --- a/sway/commands/new_window.c +++ /dev/null @@ -1,13 +0,0 @@ -#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/no_focus.c b/sway/commands/no_focus.c index ccfdec824..07e824a16 100644 --- a/sway/commands/no_focus.c +++ b/sway/commands/no_focus.c @@ -13,21 +13,12 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, "%s", err_str); + error = cmd_results_new(CMD_INVALID, err_str); free(err_str); return error; } - criteria->type = CT_NO_FOCUS; - - // Check if it already exists - if (criteria_already_exists(criteria)) { - sway_log(SWAY_DEBUG, "no_focus already exists: '%s'", criteria->raw); - criteria_destroy(criteria); - return cmd_results_new(CMD_SUCCESS, NULL); - } - list_add(config->criteria, criteria); return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index de058c6b8..14a070518 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -1,14 +1,21 @@ #include #include -#include #include "sway/commands.h" -#include "sway/tree/container.h" -#include "sway/output.h" +#include "sway/tree/view.h" #include "log.h" +static bool parse_opacity(const char *opacity, float *val) { + char *err; + *val = strtof(opacity, &err); + if (*val < 0 || *val > 1 || *err) { + return false; + } + return true; +} + struct cmd_results *cmd_opacity(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "opacity", EXPECTED_AT_LEAST, 1))) { + if ((error = checkarg(argc, "opacity", EXPECTED_EQUAL_TO, 1))) { return error; } @@ -18,28 +25,15 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "No current container"); } - char *err; - float val = strtof(argc == 1 ? argv[0] : argv[1], &err); - if (*err) { - return cmd_results_new(CMD_INVALID, "opacity float invalid"); - } + float opacity = 0.0f; - if (!strcasecmp(argv[0], "plus")) { - val = con->alpha + val; - } else if (!strcasecmp(argv[0], "minus")) { - val = con->alpha - val; - } else if (argc > 1 && strcasecmp(argv[0], "set")) { + if (!parse_opacity(argv[0], &opacity)) { return cmd_results_new(CMD_INVALID, - "Expected: set|plus|minus <0..1>: %s", argv[0]); + "Invalid value (expected 0..1): %s", argv[0]); } - if (val < 0 || val > 1) { - return cmd_results_new(CMD_FAILURE, "opacity value out of bounds"); - } - - con->alpha = val; - output_configure_scene(NULL, &con->scene_tree->node, 1); - container_update(con); + con->alpha = opacity; + container_damage_whole(con); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/output.c b/sway/commands/output.c index afff23f6a..40dbf3ca4 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -6,31 +6,19 @@ #include "log.h" // must be in order for the bsearch -static const struct cmd_handler output_handlers[] = { - { "adaptive_sync", output_cmd_adaptive_sync }, - { "allow_tearing", output_cmd_allow_tearing }, +static struct cmd_handler output_handlers[] = { { "background", output_cmd_background }, { "bg", output_cmd_background }, - { "color_profile", output_cmd_color_profile }, { "disable", output_cmd_disable }, { "dpms", output_cmd_dpms }, { "enable", output_cmd_enable }, - { "hdr", output_cmd_hdr }, - { "max_render_time", output_cmd_max_render_time }, { "mode", output_cmd_mode }, - { "modeline", output_cmd_modeline }, { "pos", output_cmd_position }, { "position", output_cmd_position }, - { "power", output_cmd_power }, - { "render_bit_depth", output_cmd_render_bit_depth }, { "res", output_cmd_mode }, { "resolution", output_cmd_mode }, { "scale", output_cmd_scale }, - { "scale_filter", output_cmd_scale_filter }, - { "subpixel", output_cmd_subpixel }, - { "toggle", output_cmd_toggle }, { "transform", output_cmd_transform }, - { "unplug", output_cmd_unplug }, }; struct cmd_results *cmd_output(int argc, char **argv) { @@ -39,39 +27,14 @@ struct cmd_results *cmd_output(int argc, char **argv) { return error; } - // The HEADLESS-1 output is a dummy output used when there's no outputs + // The NOOP-1 output is a dummy output used when there's no outputs // connected. It should never be configured. - if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) { + if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { return cmd_results_new(CMD_FAILURE, "Refusing to configure the no op output"); } - struct output_config *output = NULL; - if (strcmp(argv[0], "-") == 0 || strcmp(argv[0], "--") == 0) { - if (config->reading) { - return cmd_results_new(CMD_FAILURE, - "Current output alias (%s) cannot be used in the config", - argv[0]); - } - struct sway_output *sway_output = config->handler_context.node ? - node_get_output(config->handler_context.node) : NULL; - if (!sway_output) { - return cmd_results_new(CMD_FAILURE, "Unknown output"); - } - if (sway_output == root->fallback_output) { - return cmd_results_new(CMD_FAILURE, - "Refusing to configure the no op output"); - } - if (strcmp(argv[0], "-") == 0) { - output = new_output_config(sway_output->wlr_output->name); - } else { - char identifier[128]; - output_get_identifier(identifier, 128, sway_output); - output = new_output_config(identifier); - } - } else { - output = new_output_config(argv[0]); - } + struct output_config *output = new_output_config(argv[0]); if (!output) { sway_log(SWAY_ERROR, "Failed to allocate output config"); return NULL; @@ -104,20 +67,13 @@ 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); - store_output_config(output); - - if (config->reading) { - // When reading the config file, we wait till the end to do a single - // modeset and swaybg spawn. - return cmd_results_new(CMD_SUCCESS, NULL); - } - request_modeset(); - - if (background && !spawn_swaybg()) { - return cmd_results_new(CMD_FAILURE, - "Failed to apply background configuration"); + // If reloading, the output configs will be applied after reading the + // entire config and before the deferred commands so that an auto generated + // workspace name is not given to re-enabled outputs. + if (!config->reloading) { + apply_output_config_to_outputs(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/output/adaptive_sync.c b/sway/commands/output/adaptive_sync.c deleted file mode 100644 index 4ce88f885..000000000 --- a/sway/commands/output/adaptive_sync.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" -#include "util.h" - -struct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - if (argc == 0) { - return cmd_results_new(CMD_INVALID, "Missing adaptive_sync argument"); - } - - bool current_value = true; - if (strcasecmp(argv[0], "toggle") == 0) { - const char *oc_name = config->handler_context.output_config->name; - if (strcmp(oc_name, "*") == 0) { - return cmd_results_new(CMD_INVALID, - "Cannot apply toggle to all outputs"); - } - - struct sway_output *sway_output = all_output_by_name_or_id(oc_name); - if (!sway_output || !sway_output->wlr_output) { - return cmd_results_new(CMD_FAILURE, - "Cannot apply toggle to unknown output %s", oc_name); - } - - current_value = - sway_output->wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; - } - - config->handler_context.output_config->adaptive_sync = parse_boolean(argv[0], current_value); - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - return NULL; -} diff --git a/sway/commands/output/allow_tearing.c b/sway/commands/output/allow_tearing.c deleted file mode 100644 index 9a183b96c..000000000 --- a/sway/commands/output/allow_tearing.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "sway/commands.h" -#include "sway/config.h" -#include "util.h" - -struct cmd_results *output_cmd_allow_tearing(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - if (argc == 0) { - return cmd_results_new(CMD_INVALID, "Missing allow_tearing argument"); - } - - if (parse_boolean(argv[0], - (config->handler_context.output_config->allow_tearing == 1))) { - config->handler_context.output_config->allow_tearing = 1; - } else { - config->handler_context.output_config->allow_tearing = 0; - } - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - return NULL; -} diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index fe1621626..5a15ed0fc 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -1,10 +1,14 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include #include #include +#include +#include #include "sway/commands.h" #include "sway/config.h" +#include "sway/swaynag.h" #include "log.h" #include "stringop.h" @@ -16,16 +20,6 @@ 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"); @@ -40,14 +34,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } struct output_config *output = config->handler_context.output_config; - char *src = NULL; + 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"); - } - if (!(output->background = strdup(argv[0]))) goto cleanup; - if (!(output->background_option = strdup("solid_color"))) goto cleanup; + output->background = strdup(argv[0]); + output->background_option = strdup("solid_color"); output->background_fallback = NULL; argc -= 2; argv += 2; } else { @@ -75,76 +65,83 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing background file"); } - if (!(src = join_args(argv, j))) goto cleanup; - if (!expand_path(&src)) { + wordexp_t p = {0}; + char *src = join_args(argv, j); + while (strstr(src, " ")) { + src = realloc(src, strlen(src) + 2); + char *ptr = strstr(src, " ") + 1; + memmove(ptr + 1, ptr, strlen(ptr) + 1); + *ptr = '\\'; + } + if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, "Invalid syntax (%s)", src); free(src); + wordfree(&p); return cmd_res; } + free(src); + src = join_args(p.we_wordv, p.we_wordc); + wordfree(&p); + if (!src) { + sway_log(SWAY_ERROR, "Failed to duplicate string"); + return cmd_results_new(CMD_FAILURE, "Unable to allocate resource"); + } if (config->reading && *src != '/') { // src file is inside configuration dir char *conf = strdup(config->current_config_path); - if (!conf) goto cleanup; - - char *conf_path = dirname(conf); - char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); - if (!real_src) { - free(conf); - goto cleanup; + if (!conf) { + sway_log(SWAY_ERROR, "Failed to duplicate string"); + free(src); + return cmd_results_new(CMD_FAILURE, + "Unable to allocate resources"); } - snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); - free(src); + char *conf_path = dirname(conf); + char *rel_path = src; + src = malloc(strlen(conf_path) + strlen(src) + 2); + if (!src) { + free(rel_path); + free(conf); + sway_log(SWAY_ERROR, "Unable to allocate memory"); + return cmd_results_new(CMD_FAILURE, + "Unable to allocate resources"); + } + + sprintf(src, "%s/%s", conf_path, rel_path); + free(rel_path); free(conf); - src = real_src; } bool can_access = access(src, F_OK) != -1; - argc -= j + 1; argv += j + 1; - free(output->background_option); - free(output->background_fallback); - free(output->background); - output->background = output->background_option = output->background_fallback = NULL; - char *fallback = NULL; - - if (argc && *argv[0] == '#') { - if (validate_color(argv[0])) { - if (!(fallback = strdup(argv[0]))) goto cleanup; - output->background_fallback = fallback; - } else { - sway_log(SWAY_ERROR, "fallback '%s' should be of the form #RRGGBB", argv[0]); - config_add_swaynag_warning("fallback '%s' should be of the form #RRGGBB\n", argv[0]); - } - argc--; argv++; - } - if (!can_access) { - if (!fallback) { - sway_log(SWAY_ERROR, "Unable to access background file '%s' " - "and no valid fallback provided", src); - struct cmd_results *res = cmd_results_new(CMD_FAILURE, "Unable to access " - "background file '%s' and no valid fallback provided", src); - free(src); - return res; - } - sway_log(SWAY_DEBUG, "Cannot access file '%s', using fallback '%s'", src, fallback); - output->background = fallback; - if (!(output->background_option = strdup("solid_color"))) goto cleanup; - output->background_fallback = NULL; + sway_log_errno(SWAY_ERROR, "Unable to access background file '%s'", + src); + config_add_swaynag_warning("Unable to access background file '%s'", + src); + free(src); } else { output->background = src; - if (!(output->background_option = strdup(mode))) goto cleanup; + output->background_option = strdup(mode); + } + argc -= j + 1; argv += j + 1; + + output->background_fallback = NULL; + if (argc && *argv[0] == '#') { + output->background_fallback = strdup(argv[0]); + argc--; argv++; + + if (!can_access) { + output->background = output->background_fallback; + output->background_option = strdup("solid_color"); + output->background_fallback = NULL; + } } } + config->handler_context.leftovers.argc = argc; config->handler_context.leftovers.argv = argv; return NULL; - -cleanup: - free(src); - sway_log(SWAY_ERROR, "Failed to allocate resources"); - return cmd_results_new(CMD_FAILURE, "Unable to allocate resources"); } diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c deleted file mode 100644 index 0456b19b7..000000000 --- a/sway/commands/output/color_profile.c +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include -#include -#include -#include -#include "sway/commands.h" -#include "sway/config.h" -#include "stringop.h" - -static bool read_file_into_buf(const char *path, void **buf, size_t *size) { - /* Why not use fopen/fread directly? glibc will succesfully open directories, - * not just files, and supports seeking on them. Instead, we directly - * work with file descriptors and use the more consistent open/fstat/read. */ - int fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC); - if (fd == -1) { - return false; - } - char *b = NULL; - struct stat info; - if (fstat(fd, &info) == -1) { - goto fail; - } - // only regular files, to avoid issues with e.g. opening pipes - if (!S_ISREG(info.st_mode)) { - goto fail; - } - off_t s = info.st_size; - if (s <= 0) { - goto fail; - } - b = calloc(1, s); - if (!b) { - goto fail; - } - size_t nread = 0; - while (nread < (size_t)s) { - size_t to_read = (size_t)s - nread; - ssize_t r = read(fd, b + nread, to_read); - if ((r == -1 && errno != EINTR) || r == 0) { - goto fail; - } - nread += (size_t)r; - } - close(fd); - *buf = b; - *size = (size_t)s; - return true; // success -fail: - free(b); - close(fd); - return false; -} - -struct cmd_results *output_cmd_color_profile(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - - enum color_profile new_mode = COLOR_PROFILE_TRANSFORM; - if (argc >= 2 && strcmp(*argv, "--device-primaries") == 0) { - new_mode = COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES; - argc--; - argv++; - } - - if (!argc) { - return cmd_results_new(CMD_INVALID, "Missing color_profile first argument."); - } - - if (strcmp(*argv, "gamma22") == 0) { - wlr_color_transform_unref(config->handler_context.output_config->color_transform); - config->handler_context.output_config->color_transform = NULL; - config->handler_context.output_config->color_profile = new_mode; - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - } else if (strcmp(*argv, "srgb") == 0) { - wlr_color_transform_unref(config->handler_context.output_config->color_transform); - config->handler_context.output_config->color_transform = - wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_SRGB); - config->handler_context.output_config->color_profile = new_mode; - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - } else if (strcmp(*argv, "icc") == 0) { - if (argc < 2) { - return cmd_results_new(CMD_INVALID, - "Invalid color profile specification: icc type requires a file"); - } - if (new_mode != COLOR_PROFILE_TRANSFORM) { - return cmd_results_new(CMD_INVALID, - "Invalid color profile specification: --device-primaries cannot be used with icc"); - } - - char *icc_path = strdup(argv[1]); - if (!expand_path(&icc_path)) { - struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, - "Invalid color profile specification: invalid file path"); - free(icc_path); - return cmd_res; - } - - void *data = NULL; - size_t size = 0; - if (!read_file_into_buf(icc_path, &data, &size)) { - free(icc_path); - return cmd_results_new(CMD_FAILURE, - "Failed to load color profile: could not read ICC file"); - } - free(icc_path); - - struct wlr_color_transform *tmp = - wlr_color_transform_init_linear_to_icc(data, size); - if (!tmp) { - free(data); - return cmd_results_new(CMD_FAILURE, - "Failed to load color profile: failed to initialize transform from ICC"); - } - free(data); - - wlr_color_transform_unref(config->handler_context.output_config->color_transform); - config->handler_context.output_config->color_transform = tmp; - config->handler_context.output_config->color_profile = COLOR_PROFILE_TRANSFORM; - - config->handler_context.leftovers.argc = argc - 2; - config->handler_context.leftovers.argv = argv + 2; - } else { - return cmd_results_new(CMD_INVALID, - "Invalid color profile specification: " - "first argument should be gamma22|icc|srgb"); - } - - return NULL; -} diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index c7adbd585..9d75a80e8 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c @@ -1,8 +1,22 @@ -#include "log.h" #include "sway/commands.h" +#include "sway/config.h" +#include "util.h" struct cmd_results *output_cmd_dpms(int argc, char **argv) { - sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, " - "use \"output power\" instead"); - return output_cmd_power(argc, argv); + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (!argc) { + return cmd_results_new(CMD_INVALID, "Missing dpms argument."); + } + + if (parse_boolean(argv[0], true)) { + config->handler_context.output_config->dpms_state = DPMS_ON; + } else { + config->handler_context.output_config->dpms_state = DPMS_OFF; + } + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + return NULL; } diff --git a/sway/commands/output/hdr.c b/sway/commands/output/hdr.c deleted file mode 100644 index 26d202216..000000000 --- a/sway/commands/output/hdr.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" -#include "util.h" - -struct cmd_results *output_cmd_hdr(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - if (argc == 0) { - return cmd_results_new(CMD_INVALID, "Missing hdr argument"); - } - - bool current = false; - if (strcasecmp(argv[0], "toggle") == 0) { - const char *oc_name = config->handler_context.output_config->name; - if (strcmp(oc_name, "*") == 0) { - return cmd_results_new(CMD_INVALID, - "Cannot apply toggle to all outputs"); - } - - struct sway_output *output = all_output_by_name_or_id(oc_name); - if (!output) { - return cmd_results_new(CMD_FAILURE, - "Cannot apply toggle to unknown output %s", oc_name); - } - - current = output->hdr; - } - - config->handler_context.output_config->hdr = parse_boolean(argv[0], current); - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - return NULL; -} diff --git a/sway/commands/output/max_render_time.c b/sway/commands/output/max_render_time.c deleted file mode 100644 index 2d3cebe30..000000000 --- a/sway/commands/output/max_render_time.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" - -struct cmd_results *output_cmd_max_render_time(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 max render time argument."); - } - - int max_render_time; - if (!strcmp(*argv, "off")) { - max_render_time = 0; - } else { - char *end; - max_render_time = strtol(*argv, &end, 10); - if (*end || max_render_time <= 0) { - return cmd_results_new(CMD_INVALID, "Invalid max render time."); - } - } - config->handler_context.output_config->max_render_time = max_render_time; - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - return NULL; -} diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c index 019d625a7..bcfce372d 100644 --- a/sway/commands/output/mode.c +++ b/sway/commands/output/mode.c @@ -12,17 +12,6 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { struct output_config *output = config->handler_context.output_config; - if (strcmp(argv[0], "--custom") == 0) { - argv++; - argc--; - output->custom_mode = 1; - } else { - output->custom_mode = 0; - } - - // Reset custom modeline, if any - output->drm_mode.type = 0; - char *end; output->width = strtol(*argv, &end, 10); if (*end) { @@ -61,58 +50,3 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { return NULL; } -static bool parse_modeline(char **argv, drmModeModeInfo *mode) { - mode->type = DRM_MODE_TYPE_USERDEF; - mode->clock = strtof(argv[0], NULL) * 1000; - mode->hdisplay = strtol(argv[1], NULL, 10); - mode->hsync_start = strtol(argv[2], NULL, 10); - mode->hsync_end = strtol(argv[3], NULL, 10); - mode->htotal = strtol(argv[4], NULL, 10); - mode->vdisplay = strtol(argv[5], NULL, 10); - mode->vsync_start = strtol(argv[6], NULL, 10); - mode->vsync_end = strtol(argv[7], NULL, 10); - mode->vtotal = strtol(argv[8], NULL, 10); - - mode->vrefresh = mode->clock * 1000.0 * 1000.0 - / mode->htotal / mode->vtotal; - if (strcasecmp(argv[9], "+hsync") == 0) { - mode->flags |= DRM_MODE_FLAG_PHSYNC; - } else if (strcasecmp(argv[9], "-hsync") == 0) { - mode->flags |= DRM_MODE_FLAG_NHSYNC; - } else { - return false; - } - - if (strcasecmp(argv[10], "+vsync") == 0) { - mode->flags |= DRM_MODE_FLAG_PVSYNC; - } else if (strcasecmp(argv[10], "-vsync") == 0) { - mode->flags |= DRM_MODE_FLAG_NVSYNC; - } else { - return false; - } - - snprintf(mode->name, sizeof(mode->name), "%dx%d@%d", - mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000); - - return true; -} - -struct cmd_results *output_cmd_modeline(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 modeline argument."); - } - - struct output_config *output = config->handler_context.output_config; - - if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) { - return cmd_results_new(CMD_INVALID, "Invalid modeline"); - } - - config->handler_context.leftovers.argc = argc - 12; - config->handler_context.leftovers.argv = argv + 12; - return NULL; -} - diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c deleted file mode 100644 index e6ae28520..000000000 --- a/sway/commands/output/power.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" -#include "util.h" - -struct cmd_results *output_cmd_power(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - if (argc == 0) { - return cmd_results_new(CMD_INVALID, "Missing power argument"); - } - - bool current = true; - if (strcasecmp(argv[0], "toggle") == 0) { - const char *oc_name = config->handler_context.output_config->name; - if (strcmp(oc_name, "*") == 0) { - return cmd_results_new(CMD_INVALID, - "Cannot apply toggle to all outputs"); - } - - struct sway_output *sway_output = all_output_by_name_or_id(oc_name); - if (!sway_output || !sway_output->wlr_output) { - return cmd_results_new(CMD_FAILURE, - "Cannot apply toggle to unknown output %s", oc_name); - } - - if (sway_output->enabled && !sway_output->wlr_output->enabled) { - current = false; - } - } - - if (parse_boolean(argv[0], current)) { - config->handler_context.output_config->power = 1; - } else { - config->handler_context.output_config->power = 0; - } - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - return NULL; -} diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c deleted file mode 100644 index 3fa30e045..000000000 --- a/sway/commands/output/render_bit_depth.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include "sway/commands.h" -#include "sway/config.h" - -struct cmd_results *output_cmd_render_bit_depth(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 bit depth argument."); - } - - if (strcmp(*argv, "6") == 0) { - config->handler_context.output_config->render_bit_depth = - RENDER_BIT_DEPTH_6; - } else if (strcmp(*argv, "8") == 0) { - config->handler_context.output_config->render_bit_depth = - RENDER_BIT_DEPTH_8; - } else if (strcmp(*argv, "10") == 0) { - config->handler_context.output_config->render_bit_depth = - RENDER_BIT_DEPTH_10; - } else { - return cmd_results_new(CMD_INVALID, - "Invalid bit depth. Must be a value in (6|8|10)."); - } - - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - return NULL; -} - diff --git a/sway/commands/output/scale_filter.c b/sway/commands/output/scale_filter.c deleted file mode 100644 index fa1e8e0d5..000000000 --- a/sway/commands/output/scale_filter.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include "log.h" -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" - -struct cmd_results *output_cmd_scale_filter(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 scale_filter argument."); - } - - - enum scale_filter_mode scale_filter; - if (strcmp(*argv, "linear") == 0) { - scale_filter = SCALE_FILTER_LINEAR; - } else if (strcmp(*argv, "nearest") == 0) { - scale_filter = SCALE_FILTER_NEAREST; - } else if (strcmp(*argv, "smart") == 0) { - scale_filter = SCALE_FILTER_SMART; - } else { - return cmd_results_new(CMD_INVALID, "Invalid output scale_filter."); - } - - struct output_config *oc = config->handler_context.output_config; - config->handler_context.leftovers.argc = argc - 1; - config->handler_context.leftovers.argv = argv + 1; - - oc->scale_filter = scale_filter; - return NULL; -} diff --git a/sway/commands/output/subpixel.c b/sway/commands/output/subpixel.c deleted file mode 100644 index 63191ee62..000000000 --- a/sway/commands/output/subpixel.c +++ /dev/null @@ -1,36 +0,0 @@ -#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 deleted file mode 100644 index c6b72845b..000000000 --- a/sway/commands/output/toggle.c +++ /dev/null @@ -1,37 +0,0 @@ -#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_output_config(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 8db71bb30..8613a8e78 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c @@ -1,26 +1,9 @@ #include -#include #include "sway/commands.h" #include "sway/config.h" #include "log.h" #include "sway/output.h" -static enum wl_output_transform invert_rotation_direction( - enum wl_output_transform t) { - switch (t) { - case WL_OUTPUT_TRANSFORM_90: - return WL_OUTPUT_TRANSFORM_270; - case WL_OUTPUT_TRANSFORM_270: - return WL_OUTPUT_TRANSFORM_90; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - return WL_OUTPUT_TRANSFORM_FLIPPED_270; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - return WL_OUTPUT_TRANSFORM_FLIPPED_90; - default: - return t; - } -} - struct cmd_results *output_cmd_transform(int argc, char **argv) { if (!config->handler_context.output_config) { return cmd_results_new(CMD_FAILURE, "Missing output config"); @@ -28,10 +11,8 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) { if (!argc) { return cmd_results_new(CMD_INVALID, "Missing transform argument."); } - enum wl_output_transform transform; - if (strcmp(*argv, "normal") == 0 || - strcmp(*argv, "0") == 0) { + if (strcmp(*argv, "normal") == 0) { transform = WL_OUTPUT_TRANSFORM_NORMAL; } else if (strcmp(*argv, "90") == 0) { transform = WL_OUTPUT_TRANSFORM_90; @@ -50,20 +31,11 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) { } else { return cmd_results_new(CMD_INVALID, "Invalid output transform."); } - - // Sway uses clockwise transforms, while WL_OUTPUT_TRANSFORM_* describe - // anti-clockwise transforms - transform = invert_rotation_direction(transform); - struct output_config *output = config->handler_context.output_config; config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; if (argc > 1 && (strcmp(argv[1], "clockwise") == 0 || strcmp(argv[1], "anticlockwise") == 0)) { - if (config->reloading) { - return cmd_results_new(CMD_INVALID, - "Relative transforms cannot be used in the configuration file"); - } if (!sway_assert(output->name != NULL, "Output config name not set")) { return NULL; } @@ -77,14 +49,13 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) { "Cannot apply relative transform to unknown output %s", output->name); } if (strcmp(argv[1], "anticlockwise") == 0) { - transform = invert_rotation_direction(transform); + transform = wlr_output_transform_invert(transform); } struct wlr_output *w_output = s_output->wlr_output; transform = wlr_output_transform_compose(w_output->transform, transform); config->handler_context.leftovers.argv += 1; config->handler_context.leftovers.argc -= 1; } - output->transform = transform; return NULL; } diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c deleted file mode 100644 index dfef626f1..000000000 --- a/sway/commands/output/unplug.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include -#if WLR_HAS_X11_BACKEND -#include -#endif -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" - -static bool is_backend_allowed(struct wlr_backend *backend) { - if (wlr_backend_is_headless(backend)) { - return true; - } - if (wlr_backend_is_wl(backend)) { - return true; - } -#if WLR_HAS_X11_BACKEND - if (wlr_backend_is_x11(backend)) { - return true; - } -#endif - return false; -} - -/** - * This command is intended for developer use only. - */ -struct cmd_results *output_cmd_unplug(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - - const char *oc_name = config->handler_context.output_config->name; - if (strcmp(oc_name, "*") == 0) { - return cmd_results_new(CMD_INVALID, "Won't unplug all outputs"); - } - - struct sway_output *sway_output = all_output_by_name_or_id(oc_name); - if (!sway_output) { - return cmd_results_new(CMD_INVALID, - "Cannot unplug unknown output %s", oc_name); - } - - if (!is_backend_allowed(sway_output->wlr_output->backend)) { - return cmd_results_new(CMD_INVALID, - "Can only unplug outputs with headless, wayland or x11 backend"); - } - - wlr_output_destroy(sway_output->wlr_output); - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c deleted file mode 100644 index 9e2689c21..000000000 --- a/sway/commands/primary_selection.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include "sway/config.h" -#include "sway/commands.h" -#include "util.h" - -struct cmd_results *cmd_primary_selection(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) { - return error; - } - - bool primary_selection = parse_boolean(argv[0], true); - - // config->primary_selection is reset to the previous value on reload in - // load_main_config() - if (config->reloading && config->primary_selection != primary_selection) { - return cmd_results_new(CMD_FAILURE, - "primary_selection can only be enabled/disabled at launch"); - } - - config->primary_selection = primary_selection; - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 6c0aac261..19ec065cd 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -8,8 +9,9 @@ #include "list.h" #include "log.h" -static void title_bar_update_iterator(struct sway_container *con, void *data) { - container_update_title_bar(con); +static void rebuild_textures_iterator(struct sway_container *con, void *data) { + container_update_marks_textures(con); + container_update_title_textures(con); } static void do_reload(void *data) { @@ -20,12 +22,7 @@ static void do_reload(void *data) { list_add(bar_ids, strdup(bar->id)); } - const char *path = NULL; - if (config->user_config_path) { - path = config->current_config_path; - } - - if (!load_main_config(path, true, false)) { + if (!load_main_config(config->current_config_path, true, false)) { sway_log(SWAY_ERROR, "Error(s) reloading config"); list_free_items_and_destroy(bar_ids); return; @@ -46,7 +43,8 @@ static void do_reload(void *data) { } list_free_items_and_destroy(bar_ids); - root_for_each_container(title_bar_update_iterator, NULL); + config_update_font_height(true); + root_for_each_container(rebuild_textures_iterator, NULL); arrange_root(); } @@ -57,12 +55,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) { return error; } - const char *path = NULL; - if (config->user_config_path) { - path = config->current_config_path; - } - - if (!load_main_config(path, true, true)) { + if (!load_main_config(config->current_config_path, true, true)) { return cmd_results_new(CMD_FAILURE, "Error(s) reloading config."); } diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 63fac05cc..88377b092 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -1,17 +1,14 @@ #include #include #include -#include #include "log.h" #include "stringop.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/ipc-server.h" #include "sway/output.h" -#include "sway/desktop/launcher.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" -#include "sway/tree/root.h" static const char expected_syntax[] = "Expected 'rename workspace to ' or " @@ -27,7 +24,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { "Can't run this command while there's no outputs connected."); } if (strcasecmp(argv[0], "workspace") != 0) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } int argn = 1; @@ -66,7 +63,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { ++argn; // move past "to" if (argn >= argc) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } char *new_name = join_args(argv + argn, argc - argn); @@ -92,12 +89,9 @@ struct cmd_results *cmd_rename(int argc, char **argv) { } sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); - free(workspace->name); workspace->name = new_name; - wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, workspace->name); - output_sort_workspaces(workspace->output); ipc_event_workspace(NULL, workspace, "rename"); diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 3530ee5a8..c9261535a 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -11,11 +11,100 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" -#include "util.h" #define AXIS_HORIZONTAL (WLR_EDGE_LEFT | WLR_EDGE_RIGHT) #define AXIS_VERTICAL (WLR_EDGE_TOP | WLR_EDGE_BOTTOM) +static const int MIN_SANE_W = 100, MIN_SANE_H = 60; + +enum resize_unit { + RESIZE_UNIT_PX, + RESIZE_UNIT_PPT, + RESIZE_UNIT_DEFAULT, + RESIZE_UNIT_INVALID, +}; + +struct resize_amount { + int amount; + enum resize_unit unit; +}; + +static enum resize_unit parse_resize_unit(const char *unit) { + if (strcasecmp(unit, "px") == 0) { + return RESIZE_UNIT_PX; + } + if (strcasecmp(unit, "ppt") == 0) { + return RESIZE_UNIT_PPT; + } + if (strcasecmp(unit, "default") == 0) { + return RESIZE_UNIT_DEFAULT; + } + return RESIZE_UNIT_INVALID; +} + +// Parse arguments such as "10", "10px" or "10 px". +// Returns the number of arguments consumed. +static int parse_resize_amount(int argc, char **argv, + struct resize_amount *amount) { + char *err; + amount->amount = (int)strtol(argv[0], &err, 10); + if (*err) { + // e.g. 10px + amount->unit = parse_resize_unit(err); + return 1; + } + if (argc == 1) { + amount->unit = RESIZE_UNIT_DEFAULT; + return 1; + } + // Try the second argument + amount->unit = parse_resize_unit(argv[1]); + if (amount->unit == RESIZE_UNIT_INVALID) { + amount->unit = RESIZE_UNIT_DEFAULT; + return 1; + } + 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; @@ -57,7 +146,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, (allow_last || index < siblings->length - 1)) { return con; } - con = con->pending.parent; + con = con->parent; } return NULL; @@ -75,110 +164,89 @@ void container_resize_tiled(struct sway_container *con, return; } - if (container_is_scratchpad_hidden_or_child(con)) { - return; - } - // For HORIZONTAL or VERTICAL, we are growing in two directions so select - // all adjacent siblings. For RIGHT or DOWN or LEFT or UP select just the - // previous or next sibling. - list_t *resize = create_list(); + // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. + // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to + // the previous sibling. + struct sway_container *prev = NULL; + struct sway_container *next = NULL; list_t *siblings = container_get_siblings(con); int index = container_sibling_index(con); if (axis == AXIS_HORIZONTAL || axis == AXIS_VERTICAL) { - list_cat(resize, siblings); + if (index == 0) { + next = siblings->items[1]; + } else if (index == siblings->length - 1) { + // Convert edge to top/left + next = con; + con = siblings->items[index - 1]; + amount = -amount; + } else { + prev = siblings->items[index - 1]; + next = siblings->items[index + 1]; + } } else if (axis == WLR_EDGE_TOP || axis == WLR_EDGE_LEFT) { if (!sway_assert(index > 0, "Didn't expect first child")) { - goto cleanup; + return; } - list_add(resize, siblings->items[index - 1]); - list_add(resize, con); + next = con; + con = siblings->items[index - 1]; + amount = -amount; } else { if (!sway_assert(index < siblings->length - 1, "Didn't expect last child")) { - goto cleanup; + return; } - list_add(resize, con); - list_add(resize, siblings->items[index + 1]); + next = siblings->items[index + 1]; } // Apply new dimensions - int sibling_amount = ceil((double)amount / (double)(resize->length - 1)); + int sibling_amount = prev ? amount / 2 : amount; if (is_horizontal(axis)) { - for (int i = 0; i < resize->length; i++) { - struct sway_container *sibling = resize->items[i]; - double change = sibling == con ? amount : -sibling_amount; - if (sibling->pending.width + change < MIN_SANE_W) { - goto cleanup; - } + if (con->width + amount < MIN_SANE_W) { + return; } - if (con->child_total_width <= 0) { - goto cleanup; + if (next->width - sibling_amount < MIN_SANE_W) { + return; } - - // We're going to resize so snap all the width fractions to full pixels - // to avoid rounding issues - for (int i = 0; i < siblings->length; ++i) { - struct sway_container *con = siblings->items[i]; - con->width_fraction = con->pending.width / con->child_total_width; + if (prev && prev->width - sibling_amount < MIN_SANE_W) { + return; } - - double amount_fraction = (double)amount / con->child_total_width; - double sibling_amount_fraction = - amount_fraction / (double)(resize->length - 1); - - for (int i = 0; i < resize->length; i++) { - struct sway_container *sibling = resize->items[i]; - sibling->width_fraction += - sibling == con ? amount_fraction : -sibling_amount_fraction; + con->width += amount; + next->width -= sibling_amount; + if (prev) { + prev->width -= sibling_amount; } } else { - for (int i = 0; i < resize->length; i++) { - struct sway_container *sibling = resize->items[i]; - double change = sibling == con ? amount : -sibling_amount; - if (sibling->pending.height + change < MIN_SANE_H) { - goto cleanup; - } + if (con->height + amount < MIN_SANE_H) { + return; } - if (con->child_total_height <= 0) { - goto cleanup; + if (next->height - sibling_amount < MIN_SANE_H) { + return; } - - // We're going to resize so snap all the height fractions to full pixels - // to avoid rounding issues - for (int i = 0; i < siblings->length; ++i) { - struct sway_container *con = siblings->items[i]; - con->height_fraction = con->pending.height / con->child_total_height; + if (prev && prev->height - sibling_amount < MIN_SANE_H) { + return; } - - double amount_fraction = (double)amount / con->child_total_height; - double sibling_amount_fraction = - amount_fraction / (double)(resize->length - 1); - - for (int i = 0; i < resize->length; i++) { - struct sway_container *sibling = resize->items[i]; - sibling->height_fraction += - sibling == con ? amount_fraction : -sibling_amount_fraction; + con->height += amount; + next->height -= sibling_amount; + if (prev) { + prev->height -= sibling_amount; } } - if (con->pending.parent) { - arrange_container(con->pending.parent); + if (con->parent) { + arrange_container(con->parent); } else { - arrange_workspace(con->pending.workspace); + arrange_workspace(con->workspace); } - -cleanup: - list_free(resize); } /** * Implement `resize ` for a floating container. */ static struct cmd_results *resize_adjust_floating(uint32_t axis, - struct movement_amount *amount) { + struct resize_amount *amount) { struct sway_container *con = config->handler_context.container; int grow_width = 0, grow_height = 0; @@ -190,17 +258,16 @@ 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; - floating_calculate_constraints(&min_width, &max_width, - &min_height, &max_height); - if (con->pending.width + grow_width < min_width) { - grow_width = min_width - con->pending.width; - } else if (con->pending.width + grow_width > max_width) { - grow_width = max_width - con->pending.width; + 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) { + grow_width = max_width - con->width; } - if (con->pending.height + grow_height < min_height) { - grow_height = min_height - con->pending.height; - } else if (con->pending.height + grow_height > max_height) { - grow_height = max_height - con->pending.height; + if (con->height + grow_height < min_height) { + grow_height = min_height - con->height; + } else if (con->height + grow_height > max_height) { + grow_height = max_height - con->height; } int grow_x = 0, grow_y = 0; @@ -213,18 +280,18 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, } else if (axis == WLR_EDGE_LEFT) { grow_x = -grow_width; } - if (grow_width == 0 && grow_height == 0) { + if (grow_x == 0 && grow_y == 0) { return cmd_results_new(CMD_INVALID, "Cannot resize any further"); } - con->pending.x += grow_x; - con->pending.y += grow_y; - con->pending.width += grow_width; - con->pending.height += grow_height; + con->x += grow_x; + con->y += grow_y; + con->width += grow_width; + con->height += grow_height; - con->pending.content_x += grow_x; - con->pending.content_y += grow_y; - con->pending.content_width += grow_width; - con->pending.content_height += grow_height; + con->content_x += grow_x; + con->content_y += grow_y; + con->content_width += grow_width; + con->content_height += grow_height; arrange_container(con); @@ -235,46 +302,26 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, * Implement `resize ` for a tiled container. */ static struct cmd_results *resize_adjust_tiled(uint32_t axis, - struct movement_amount *amount) { + struct resize_amount *amount) { struct sway_container *current = config->handler_context.container; - if (container_is_scratchpad_hidden_or_child(current)) { - return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); + if (amount->unit == RESIZE_UNIT_DEFAULT) { + amount->unit = RESIZE_UNIT_PPT; } - - if (amount->unit == MOVEMENT_UNIT_DEFAULT) { - amount->unit = MOVEMENT_UNIT_PPT; - } - if (amount->unit == MOVEMENT_UNIT_PPT) { - struct sway_container *parent = current->pending.parent; + if (amount->unit == RESIZE_UNIT_PPT) { float pct = amount->amount / 100.0f; if (is_horizontal(axis)) { - while (parent && parent->pending.layout != L_HORIZ) { - parent = parent->pending.parent; - } - if (parent) { - amount->amount = (float)parent->pending.width * pct; - } else { - amount->amount = (float)current->pending.workspace->width * pct; - } + amount->amount = (float)current->width * pct; } else { - while (parent && parent->pending.layout != L_VERT) { - parent = parent->pending.parent; - } - if (parent) { - amount->amount = (float)parent->pending.height * pct; - } else { - amount->amount = (float)current->pending.workspace->height * pct; - } + amount->amount = (float)current->height * pct; } } - double old_width = current->width_fraction; - double old_height = current->height_fraction; + double old_width = current->width; + double old_height = current->height; container_resize_tiled(current, axis, amount->amount); - if (current->width_fraction == old_width && - current->height_fraction == old_height) { + if (current->width == old_width && current->height == old_height) { return cmd_results_new(CMD_INVALID, "Cannot resize any further"); } return cmd_results_new(CMD_SUCCESS, NULL); @@ -284,51 +331,46 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, * Implement `resize set` for a tiled container. */ static struct cmd_results *resize_set_tiled(struct sway_container *con, - struct movement_amount *width, struct movement_amount *height) { - - if (container_is_scratchpad_hidden_or_child(con)) { - return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); - } - + struct resize_amount *width, struct resize_amount *height) { if (width->amount) { - if (width->unit == MOVEMENT_UNIT_PPT || - width->unit == MOVEMENT_UNIT_DEFAULT) { + if (width->unit == RESIZE_UNIT_PPT || + width->unit == RESIZE_UNIT_DEFAULT) { // Convert to px - struct sway_container *parent = con->pending.parent; - while (parent && parent->pending.layout != L_HORIZ) { - parent = parent->pending.parent; + struct sway_container *parent = con->parent; + while (parent && parent->layout != L_HORIZ) { + parent = parent->parent; } if (parent) { - width->amount = parent->pending.width * width->amount / 100; + width->amount = parent->width * width->amount / 100; } else { - width->amount = con->pending.workspace->width * width->amount / 100; + width->amount = con->workspace->width * width->amount / 100; } - width->unit = MOVEMENT_UNIT_PX; + width->unit = RESIZE_UNIT_PX; } - if (width->unit == MOVEMENT_UNIT_PX) { + if (width->unit == RESIZE_UNIT_PX) { container_resize_tiled(con, AXIS_HORIZONTAL, - width->amount - con->pending.width); + width->amount - con->width); } } if (height->amount) { - if (height->unit == MOVEMENT_UNIT_PPT || - height->unit == MOVEMENT_UNIT_DEFAULT) { + if (height->unit == RESIZE_UNIT_PPT || + height->unit == RESIZE_UNIT_DEFAULT) { // Convert to px - struct sway_container *parent = con->pending.parent; - while (parent && parent->pending.layout != L_VERT) { - parent = parent->pending.parent; + struct sway_container *parent = con->parent; + while (parent && parent->layout != L_VERT) { + parent = parent->parent; } if (parent) { - height->amount = parent->pending.height * height->amount / 100; + height->amount = parent->height * height->amount / 100; } else { - height->amount = con->pending.workspace->height * height->amount / 100; + height->amount = con->workspace->height * height->amount / 100; } - height->unit = MOVEMENT_UNIT_PX; + height->unit = RESIZE_UNIT_PX; } - if (height->unit == MOVEMENT_UNIT_PX) { + if (height->unit == RESIZE_UNIT_PX) { container_resize_tiled(con, AXIS_VERTICAL, - height->amount - con->pending.height); + height->amount - con->height); } } @@ -339,30 +381,29 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, * Implement `resize set` for a floating container. */ static struct cmd_results *resize_set_floating(struct sway_container *con, - struct movement_amount *width, struct movement_amount *height) { + struct resize_amount *width, struct resize_amount *height) { int min_width, max_width, min_height, max_height, grow_width = 0, grow_height = 0; - floating_calculate_constraints(&min_width, &max_width, - &min_height, &max_height); + calculate_constraints(&min_width, &max_width, &min_height, &max_height); if (width->amount) { switch (width->unit) { - case MOVEMENT_UNIT_PPT: + case RESIZE_UNIT_PPT: if (container_is_scratchpad_hidden(con)) { return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container by ppt"); } // Convert to px - width->amount = con->pending.workspace->width * width->amount / 100; - width->unit = MOVEMENT_UNIT_PX; + width->amount = con->workspace->width * width->amount / 100; + width->unit = RESIZE_UNIT_PX; // Falls through - case MOVEMENT_UNIT_PX: - case MOVEMENT_UNIT_DEFAULT: + case RESIZE_UNIT_PX: + case RESIZE_UNIT_DEFAULT: width->amount = fmax(min_width, fmin(width->amount, max_width)); - grow_width = width->amount - con->pending.width; - con->pending.x -= grow_width / 2; - con->pending.width = width->amount; + grow_width = width->amount - con->width; + con->x -= grow_width / 2; + con->width = width->amount; break; - case MOVEMENT_UNIT_INVALID: + case RESIZE_UNIT_INVALID: sway_assert(false, "invalid width unit"); break; } @@ -370,32 +411,32 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, if (height->amount) { switch (height->unit) { - case MOVEMENT_UNIT_PPT: + case RESIZE_UNIT_PPT: if (container_is_scratchpad_hidden(con)) { return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container by ppt"); } // Convert to px - height->amount = con->pending.workspace->height * height->amount / 100; - height->unit = MOVEMENT_UNIT_PX; + height->amount = con->workspace->height * height->amount / 100; + height->unit = RESIZE_UNIT_PX; // Falls through - case MOVEMENT_UNIT_PX: - case MOVEMENT_UNIT_DEFAULT: + case RESIZE_UNIT_PX: + case RESIZE_UNIT_DEFAULT: height->amount = fmax(min_height, fmin(height->amount, max_height)); - grow_height = height->amount - con->pending.height; - con->pending.y -= grow_height / 2; - con->pending.height = height->amount; + grow_height = height->amount - con->height; + con->y -= grow_height / 2; + con->height = height->amount; break; - case MOVEMENT_UNIT_INVALID: + case RESIZE_UNIT_INVALID: sway_assert(false, "invalid height unit"); break; } } - con->pending.content_x -= grow_width / 2; - con->pending.content_y -= grow_height / 2; - con->pending.content_width += grow_width; - con->pending.content_height += grow_height; + con->content_x -= grow_width / 2; + con->content_y -= grow_height / 2; + con->content_width += grow_width; + con->content_height += grow_height; arrange_container(con); @@ -419,41 +460,41 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { "'resize set [width] [px|ppt] [height] [px|ppt]'"; // Width - struct movement_amount width = {0}; + struct resize_amount width = {0}; if (argc >= 2 && !strcmp(argv[0], "width") && strcmp(argv[1], "height")) { argc--; argv++; } if (strcmp(argv[0], "height")) { - int num_consumed_args = parse_movement_amount(argc, argv, &width); + int num_consumed_args = parse_resize_amount(argc, argv, &width); argc -= num_consumed_args; argv += num_consumed_args; - if (width.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "%s", usage); + if (width.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, usage); } } // Height - struct movement_amount height = {0}; + struct resize_amount height = {0}; if (argc) { if (argc >= 2 && !strcmp(argv[0], "height")) { argc--; argv++; } - int num_consumed_args = parse_movement_amount(argc, argv, &height); + int num_consumed_args = parse_resize_amount(argc, argv, &height); if (argc > num_consumed_args) { - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, usage); } - if (height.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "%s", usage); + if (width.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, usage); } } // If 0, don't resize that dimension struct sway_container *con = config->handler_context.container; if (width.amount <= 0) { - width.amount = con->pending.width; + width.amount = con->width; } if (height.amount <= 0) { - height.amount = con->pending.height; + height.amount = con->height; } if (container_is_floating(con)) { @@ -475,45 +516,45 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, "[ px|ppt [or px|ppt]]'"; uint32_t axis = parse_resize_axis(*argv); if (axis == WLR_EDGE_NONE) { - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, usage); } --argc; ++argv; // First amount - struct movement_amount first_amount; + struct resize_amount first_amount; if (argc) { - int num_consumed_args = parse_movement_amount(argc, argv, &first_amount); + int num_consumed_args = parse_resize_amount(argc, argv, &first_amount); argc -= num_consumed_args; argv += num_consumed_args; - if (first_amount.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "%s", usage); + if (first_amount.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, usage); } } else { first_amount.amount = 10; - first_amount.unit = MOVEMENT_UNIT_DEFAULT; + first_amount.unit = RESIZE_UNIT_DEFAULT; } // "or" if (argc) { if (strcmp(*argv, "or") != 0) { - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, usage); } --argc; ++argv; } // Second amount - struct movement_amount second_amount; + struct resize_amount second_amount; if (argc) { - int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); + int num_consumed_args = parse_resize_amount(argc, argv, &second_amount); if (argc > num_consumed_args) { - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, usage); } - if (second_amount.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "%s", usage); + if (second_amount.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, usage); } } else { second_amount.amount = 0; - second_amount.unit = MOVEMENT_UNIT_INVALID; + second_amount.unit = RESIZE_UNIT_INVALID; } first_amount.amount *= multiplier; @@ -523,13 +564,13 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, if (container_is_floating(con)) { // Floating containers can only resize in px. Choose an amount which // uses px, with fallback to an amount that specified no unit. - if (first_amount.unit == MOVEMENT_UNIT_PX) { + if (first_amount.unit == RESIZE_UNIT_PX) { return resize_adjust_floating(axis, &first_amount); - } else if (second_amount.unit == MOVEMENT_UNIT_PX) { + } else if (second_amount.unit == RESIZE_UNIT_PX) { return resize_adjust_floating(axis, &second_amount); - } else if (first_amount.unit == MOVEMENT_UNIT_DEFAULT) { + } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { return resize_adjust_floating(axis, &first_amount); - } else if (second_amount.unit == MOVEMENT_UNIT_DEFAULT) { + } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { return resize_adjust_floating(axis, &second_amount); } else { return cmd_results_new(CMD_INVALID, @@ -538,13 +579,13 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, } // For tiling, prefer ppt -> default -> px - if (first_amount.unit == MOVEMENT_UNIT_PPT) { + if (first_amount.unit == RESIZE_UNIT_PPT) { return resize_adjust_tiled(axis, &first_amount); - } else if (second_amount.unit == MOVEMENT_UNIT_PPT) { + } else if (second_amount.unit == RESIZE_UNIT_PPT) { return resize_adjust_tiled(axis, &second_amount); - } else if (first_amount.unit == MOVEMENT_UNIT_DEFAULT) { + } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { return resize_adjust_tiled(axis, &first_amount); - } else if (second_amount.unit == MOVEMENT_UNIT_DEFAULT) { + } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { return resize_adjust_tiled(axis, &second_amount); } else { return resize_adjust_tiled(axis, &first_amount); @@ -579,5 +620,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) { const char usage[] = "Expected 'resize " " [] [px|ppt]'"; - return cmd_results_new(CMD_INVALID, "%s", usage); + return cmd_results_new(CMD_INVALID, usage); } diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 8a63740c3..71afa306d 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -12,17 +12,12 @@ 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. if (focus && container_is_floating_or_child(focus)) { - while (focus->pending.parent) { - focus = focus->pending.parent; + while (focus->parent) { + focus = focus->parent; } } @@ -52,7 +47,7 @@ static void scratchpad_toggle_auto(void) { // In this case we move it to the current workspace. for (int i = 0; i < root->scratchpad->length; ++i) { struct sway_container *con = root->scratchpad->items[i]; - if (con->pending.parent) { + if (con->parent) { sway_log(SWAY_DEBUG, "Moving a visible scratchpad window (%s) to this workspace", con->title); @@ -80,7 +75,7 @@ static void scratchpad_toggle_container(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *ws = seat_get_focused_workspace(seat); // Check if it matches a currently visible scratchpad window and hide it. - if (con->pending.workspace && ws == con->pending.workspace) { + if (con->workspace && ws == con->workspace) { root_scratchpad_hide(con); return; } @@ -105,23 +100,22 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); } - if (config->handler_context.node_overridden) { + if (config->handler_context.using_criteria) { struct sway_container *con = config->handler_context.container; // If the container is in a floating split container, // operate on the split container instead of the child. - if (con && container_is_floating_or_child(con)) { - while (con->pending.parent) { - con = con->pending.parent; + if (container_is_floating_or_child(con)) { + while (con->parent) { + con = con->parent; } } // If using criteria, this command is executed for every container which // matches the criteria. If this container isn't in the scratchpad, - // we'll return an error. The same is true if the - // overridden node is not a container. - if (!con || !con->scratchpad) { - return cmd_results_new(CMD_INVALID, "Container is not in scratchpad."); + // we'll just silently return a success. + if (!con->scratchpad) { + return cmd_results_new(CMD_SUCCESS, NULL); } scratchpad_toggle_container(con); } else { diff --git a/sway/commands/seat.c b/sway/commands/seat.c index 2d197b692..aa36ba955 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c @@ -7,49 +7,14 @@ #include "stringop.h" // must be in order for the bsearch -// these handlers perform actions on the seat -static const struct cmd_handler seat_action_handlers[] = { - { "cursor", seat_cmd_cursor }, -}; - -// must be in order for the bsearch -// these handlers alter the seat config -static const struct cmd_handler seat_handlers[] = { +static struct cmd_handler seat_handlers[] = { { "attach", seat_cmd_attach }, + { "cursor", seat_cmd_cursor }, { "fallback", seat_cmd_fallback }, { "hide_cursor", seat_cmd_hide_cursor }, - { "idle_inhibit", seat_cmd_idle_inhibit }, - { "idle_wake", seat_cmd_idle_wake }, - { "keyboard_grouping", seat_cmd_keyboard_grouping }, { "pointer_constraint", seat_cmd_pointer_constraint }, - { "shortcuts_inhibitor", seat_cmd_shortcuts_inhibitor }, - { "xcursor_theme", seat_cmd_xcursor_theme }, }; -static struct cmd_results *action_handlers(int argc, char **argv) { - struct cmd_results *res = config_subcommand(argv, argc, - seat_action_handlers, sizeof(seat_action_handlers)); - free_seat_config(config->handler_context.seat_config); - config->handler_context.seat_config = NULL; - return res; -} - -static struct cmd_results *config_handlers(int argc, char **argv) { - struct cmd_results *res = config_subcommand(argv, argc, - seat_handlers, sizeof(seat_handlers)); - if (res && res->status != CMD_SUCCESS) { - free_seat_config(config->handler_context.seat_config); - } else { - struct seat_config *sc = - store_seat_config(config->handler_context.seat_config); - if (!config->reading) { - input_manager_apply_seat_config(sc); - } - } - config->handler_context.seat_config = NULL; - return res; -} - struct cmd_results *cmd_seat(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 2))) { @@ -70,12 +35,20 @@ struct cmd_results *cmd_seat(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Couldn't allocate config"); } - struct cmd_results *res = NULL; - if (find_handler(argv[1], seat_action_handlers, - sizeof(seat_action_handlers))) { - res = action_handlers(argc - 1, argv + 1); - } else { - res = config_handlers(argc - 1, argv + 1); + struct cmd_results *res = config_subcommand(argv + 1, argc - 1, + seat_handlers, sizeof(seat_handlers)); + if (res && res->status != CMD_SUCCESS) { + free_seat_config(config->handler_context.seat_config); + config->handler_context.seat_config = NULL; + return res; } + + struct seat_config *sc = + store_seat_config(config->handler_context.seat_config); + if (!config->reading) { + input_manager_apply_seat_config(sc); + } + + config->handler_context.seat_config = NULL; return res ? res : cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c index 47d18546b..7615eef9b 100644 --- a/sway/commands/seat/attach.c +++ b/sway/commands/seat/attach.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -11,7 +12,7 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) { if (!config->handler_context.seat_config) { return cmd_results_new(CMD_FAILURE, "No seat defined"); } - if (!config->active) { + if (config->reading) { return cmd_results_new(CMD_DEFER, NULL); } diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 434e6bbb9..085e6a33f 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include @@ -5,7 +6,6 @@ #include #include "sway/commands.h" #include "sway/input/cursor.h" -#include "sway/server.h" static struct cmd_results *press_or_release(struct sway_cursor *cursor, char *action, char *button_str); @@ -18,26 +18,24 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, int argc, char **argv) { if (strcasecmp(argv[0], "move") == 0) { if (argc < 3) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } int delta_x = strtol(argv[1], NULL, 10); int delta_y = strtol(argv[2], NULL, 10); wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y); cursor_rebase(cursor); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else if (strcasecmp(argv[0], "set") == 0) { if (argc < 3) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } // map absolute coords (0..1,0..1) to root container coords float x = strtof(argv[1], NULL) / root->width; float y = strtof(argv[2], NULL) / root->height; wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); cursor_rebase(cursor); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else { if (argc < 2) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } struct cmd_results *error = NULL; if ((error = press_or_release(cursor, argv[0], argv[1]))) { @@ -45,7 +43,6 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, } } - cursor_handle_activity_from_idle_source(cursor, IDLE_SOURCE_POINTER); return cmd_results_new(CMD_SUCCESS, NULL); } @@ -74,7 +71,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { error = handle_command(seat->cursor, argc, argv); - if (error && error->status != CMD_SUCCESS) { + if ((error && error->status != CMD_SUCCESS)) { break; } } @@ -85,47 +82,45 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { static struct cmd_results *press_or_release(struct sway_cursor *cursor, char *action, char *button_str) { - enum wl_pointer_button_state state; + enum wlr_button_state state; uint32_t button; if (strcasecmp(action, "press") == 0) { - state = WL_POINTER_BUTTON_STATE_PRESSED; + state = WLR_BUTTON_PRESSED; } else if (strcasecmp(action, "release") == 0) { - state = WL_POINTER_BUTTON_STATE_RELEASED; + state = WLR_BUTTON_RELEASED; } else { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } char *message = NULL; button = get_mouse_button(button_str, &message); if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, "%s", message); + cmd_results_new(CMD_INVALID, message); free(message); return error; } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { // Dispatch axis event - enum wl_pointer_axis orientation = + enum wlr_axis_orientation orientation = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) - ? WL_POINTER_AXIS_VERTICAL_SCROLL - : WL_POINTER_AXIS_HORIZONTAL_SCROLL; + ? WLR_AXIS_ORIENTATION_VERTICAL + : WLR_AXIS_ORIENTATION_HORIZONTAL; double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) ? -1 : 1; - struct wlr_pointer_axis_event event = { - .pointer = NULL, + struct wlr_event_pointer_axis event = { + .device = NULL, .time_msec = 0, - .source = WL_POINTER_AXIS_SOURCE_WHEEL, + .source = WLR_AXIS_SOURCE_WHEEL, .orientation = orientation, .delta = delta * 15, .delta_discrete = delta }; dispatch_cursor_axis(cursor, &event); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); return cmd_results_new(CMD_SUCCESS, NULL); } else if (!button) { return cmd_results_new(CMD_INVALID, "Unknown button %s", button_str); } dispatch_cursor_button(cursor, NULL, 0, button, state); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c index f5177a471..3bfce6977 100644 --- a/sway/commands/seat/hide_cursor.c +++ b/sway/commands/seat/hide_cursor.c @@ -1,49 +1,28 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" #include "sway/input/seat.h" -#include "sway/input/cursor.h" -#include "sway/server.h" #include "stringop.h" -#include "util.h" struct cmd_results *seat_cmd_hide_cursor(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "hide_cursor", EXPECTED_AT_LEAST, 1))) { + if ((error = checkarg(argc, "hide_cursor", EXPECTED_EQUAL_TO, 1))) { return error; } - if ((error = checkarg(argc, "hide_cursor", EXPECTED_AT_MOST, 2))) { - return error; - } - struct seat_config *seat_config = config->handler_context.seat_config; - if (!seat_config) { + if (!config->handler_context.seat_config) { return cmd_results_new(CMD_FAILURE, "No seat defined"); } - if (argc == 1) { - char *end; - int timeout = strtol(argv[0], &end, 10); - if (*end) { - return cmd_results_new(CMD_INVALID, "Expected an integer timeout"); - } - if (timeout < 100 && timeout != 0) { - timeout = 100; - } - seat_config->hide_cursor_timeout = timeout; - } else { - if (strcmp(argv[0], "when-typing") != 0) { - return cmd_results_new(CMD_INVALID, - "Expected 'hide_cursor |when-typing [enable|disable]'"); - } - seat_config->hide_cursor_when_typing = parse_boolean(argv[1], true) ? - HIDE_WHEN_TYPING_ENABLE : HIDE_WHEN_TYPING_DISABLE; - - // Invalidate all the caches for this config - struct sway_seat *seat = NULL; - wl_list_for_each(seat, &server.input->seats, link) { - seat->cursor->hide_when_typing = HIDE_WHEN_TYPING_DEFAULT; - } + char *end; + int timeout = strtol(argv[0], &end, 10); + if (*end) { + return cmd_results_new(CMD_INVALID, "Expected an integer timeout"); } + if (timeout < 100 && timeout != 0) { + timeout = 100; + } + config->handler_context.seat_config->hide_cursor_timeout = timeout; return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c deleted file mode 100644 index 2974453e9..000000000 --- a/sway/commands/seat/idle.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include -#include "log.h" -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/input/seat.h" - -static const struct { - const char *name; - uint32_t value; -} idle_source_strings[] = { - { "keyboard", IDLE_SOURCE_KEYBOARD }, - { "pointer", IDLE_SOURCE_POINTER }, - { "touch", IDLE_SOURCE_TOUCH }, - { "tablet_pad", IDLE_SOURCE_TABLET_PAD }, - { "tablet_tool", IDLE_SOURCE_TABLET_TOOL }, - { "switch", IDLE_SOURCE_SWITCH }, -}; - -static uint32_t parse_sources(int argc, char **argv) { - uint32_t sources = 0; - for (int i = 0; i < argc; ++i) { - uint32_t value = 0; - for (size_t j = 0; j < sizeof(idle_source_strings) - / sizeof(idle_source_strings[0]); ++j) { - if (strcasecmp(idle_source_strings[j].name, argv[i]) == 0) { - value = idle_source_strings[j].value; - break; - } - } - if (value == 0) { - return UINT32_MAX; - } - sources |= value; - } - return sources; -} - -struct cmd_results *seat_cmd_idle_inhibit(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "idle_inhibit", EXPECTED_AT_LEAST, 1))) { - return error; - } - if (!config->handler_context.seat_config) { - return cmd_results_new(CMD_FAILURE, "No seat defined"); - } - - uint32_t sources = parse_sources(argc, argv); - if (sources == UINT32_MAX) { - return cmd_results_new(CMD_FAILURE, "Invalid idle source"); - } - config->handler_context.seat_config->idle_inhibit_sources = sources; - return cmd_results_new(CMD_SUCCESS, NULL); -} - -struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "idle_wake", EXPECTED_AT_LEAST, 1))) { - return error; - } - if (!config->handler_context.seat_config) { - return cmd_results_new(CMD_FAILURE, "No seat defined"); - } - - uint32_t sources = parse_sources(argc, argv); - if (sources == UINT32_MAX) { - return cmd_results_new(CMD_FAILURE, "Invalid idle source"); - } - config->handler_context.seat_config->idle_wake_sources = sources; - sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated"); - if (config->reading) { - config_add_swaynag_warning("seat idle_wake is deprecated. " - "Only seat idle_inhibit is supported."); - } - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/seat/keyboard_grouping.c b/sway/commands/seat/keyboard_grouping.c deleted file mode 100644 index 5ad0f4081..000000000 --- a/sway/commands/seat/keyboard_grouping.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" -#include "stringop.h" - -struct cmd_results *seat_cmd_keyboard_grouping(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "keyboard_grouping", EXPECTED_EQUAL_TO, 1))) { - return error; - } - if (!config->handler_context.seat_config) { - return cmd_results_new(CMD_INVALID, "No seat defined"); - } - - struct seat_config *seat_config = config->handler_context.seat_config; - if (strcmp(argv[0], "none") == 0) { - seat_config->keyboard_grouping = KEYBOARD_GROUP_NONE; - } else if (strcmp(argv[0], "smart") == 0) { - seat_config->keyboard_grouping = KEYBOARD_GROUP_SMART; - } else { - return cmd_results_new(CMD_INVALID, - "Expected syntax `keyboard_grouping none|smart`"); - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/seat/pointer_constraint.c b/sway/commands/seat/pointer_constraint.c index 38f85bcd6..3890ebde0 100644 --- a/sway/commands/seat/pointer_constraint.c +++ b/sway/commands/seat/pointer_constraint.c @@ -4,7 +4,6 @@ #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" -#include "sway/server.h" enum operation { OP_ENABLE, diff --git a/sway/commands/seat/shortcuts_inhibitor.c b/sway/commands/seat/shortcuts_inhibitor.c deleted file mode 100644 index df68618dc..000000000 --- a/sway/commands/seat/shortcuts_inhibitor.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "log.h" -#include "sway/commands.h" -#include "sway/input/seat.h" -#include "sway/input/input-manager.h" -#include "sway/server.h" -#include "util.h" - -static struct cmd_results *handle_action(struct seat_config *sc, - struct sway_seat *seat, const char *action) { - struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = NULL; - if (strcmp(action, "disable") == 0) { - sc->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE; - - wl_list_for_each(sway_inhibitor, - &seat->keyboard_shortcuts_inhibitors, link) { - wlr_keyboard_shortcuts_inhibitor_v1_deactivate( - sway_inhibitor->inhibitor); - } - - sway_log(SWAY_DEBUG, "Deactivated all keyboard shortcuts inhibitors"); - } else { - sway_inhibitor = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); - if (!sway_inhibitor) { - return cmd_results_new(CMD_FAILURE, - "No inhibitor found for focused surface"); - } - - struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = - sway_inhibitor->inhibitor; - bool inhibit; - if (strcmp(action, "activate") == 0) { - inhibit = true; - } else if (strcmp(action, "deactivate") == 0) { - inhibit = false; - } else if (strcmp(action, "toggle") == 0) { - inhibit = !inhibitor->active; - } else { - return cmd_results_new(CMD_INVALID, "Expected enable|" - "disable|activate|deactivate|toggle"); - } - - if (inhibit) { - wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor); - } else { - wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor); - } - - sway_log(SWAY_DEBUG, "%sctivated keyboard shortcuts inhibitor", - inhibit ? "A" : "Dea"); - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} - -// shortcuts_inhibitor [enable|disable|activate|deactivate|toggle] -struct cmd_results *seat_cmd_shortcuts_inhibitor(int argc, char **argv) { - struct cmd_results *error = - checkarg(argc, "shortcuts_inhibitor", EXPECTED_EQUAL_TO, 1); - if (error) { - return error; - } - - struct seat_config *sc = config->handler_context.seat_config; - if (!sc) { - return cmd_results_new(CMD_FAILURE, "No seat defined"); - } - - if (strcmp(argv[0], "enable") == 0) { - sc->shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; - // at runtime disable is an action that also deactivates all active - // inhibitors handled in handle_action() - } else if (strcmp(argv[0], "disable") == 0 && !config->active) { - sc->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE; - } else if (!config->active) { - return cmd_results_new(CMD_INVALID, "only enable and disable " - "can be used in the config"); - } else { - if (strcmp(sc->name, "*") != 0) { - struct sway_seat *seat = input_manager_get_seat(sc->name, false); - if (!seat) { - return cmd_results_new(CMD_FAILURE, - "Seat %s does not exist", sc->name); - } - error = handle_action(sc, seat, argv[0]); - } else { - struct sway_seat *seat = NULL; - wl_list_for_each(seat, &server.input->seats, link) { - error = handle_action(sc, seat, argv[0]); - if (error && error->status != CMD_SUCCESS) { - break; - } - } - } - } - - return error ? error : cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c deleted file mode 100644 index 61322a577..000000000 --- a/sway/commands/seat/xcursor_theme.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include "sway/commands.h" -#include "sway/config.h" - -struct cmd_results *seat_cmd_xcursor_theme(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "xcursor_theme", EXPECTED_AT_LEAST, 1)) || - (error = checkarg(argc, "xcursor_theme", EXPECTED_AT_MOST, 2))) { - return error; - } - if (!config->handler_context.seat_config) { - return cmd_results_new(CMD_FAILURE, "No seat defined"); - } - - const char *theme_name = argv[0]; - unsigned size = 24; - - if (argc == 2) { - char *end; - size = strtoul(argv[1], &end, 10); - if (*end) { - return cmd_results_new( - CMD_INVALID, "Expected a positive integer size"); - } - } - - free(config->handler_context.seat_config->xcursor_theme.name); - config->handler_context.seat_config->xcursor_theme.name = strdup(theme_name); - config->handler_context.seat_config->xcursor_theme.size = size; - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/set.c b/sway/commands/set.c index ba384c7cb..c539e9fc7 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/commands/shortcuts_inhibitor.c b/sway/commands/shortcuts_inhibitor.c deleted file mode 100644 index 2dfd1b9f9..000000000 --- a/sway/commands/shortcuts_inhibitor.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include "log.h" -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/input/seat.h" -#include "sway/server.h" -#include "sway/tree/container.h" -#include "sway/tree/view.h" - -struct cmd_results *cmd_shortcuts_inhibitor(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "shortcuts_inhibitor", 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 shortcuts inhibitors"); - } - - struct sway_view *view = con->view; - if (strcmp(argv[0], "enable") == 0) { - view->shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; - } else if (strcmp(argv[0], "disable") == 0) { - view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE; - - struct sway_seat *seat = NULL; - wl_list_for_each(seat, &server.input->seats, link) { - struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = - keyboard_shortcuts_inhibitor_get_for_surface( - seat, view->surface); - if (!sway_inhibitor) { - continue; - } - - wlr_keyboard_shortcuts_inhibitor_v1_deactivate( - sway_inhibitor->inhibitor); - sway_log(SWAY_DEBUG, "Deactivated keyboard shortcuts " - "inhibitor for seat %s on view", - seat->wlr_seat->name); - - } - } else { - return cmd_results_new(CMD_INVALID, - "Expected `shortcuts_inhibitor enable|disable`"); - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index 60cef9fa6..0d373b80c 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -9,8 +10,8 @@ #include "stringop.h" #include "util.h" -static void title_bar_update_iterator(struct sway_container *con, void *data) { - container_update_marks(con); +static void rebuild_marks_iterator(struct sway_container *con, void *data) { + container_update_marks_textures(con); } struct cmd_results *cmd_show_marks(int argc, char **argv) { @@ -22,7 +23,12 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { config->show_marks = parse_boolean(argv[0], config->show_marks); if (config->show_marks) { - root_for_each_container(title_bar_update_iterator, NULL); + root_for_each_container(rebuild_marks_iterator, NULL); + } + + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/smart_borders.c b/sway/commands/smart_borders.c index 738786799..be3461064 100644 --- a/sway/commands/smart_borders.c +++ b/sway/commands/smart_borders.c @@ -10,12 +10,14 @@ struct cmd_results *cmd_smart_borders(int argc, char **argv) { return error; } + enum edge_border_types saved = config->hide_edge_borders; if (strcmp(argv[0], "no_gaps") == 0) { - config->hide_edge_borders_smart = ESMART_NO_GAPS; + config->hide_edge_borders = E_SMART_NO_GAPS; } else { - config->hide_edge_borders_smart = parse_boolean(argv[0], true) ? - ESMART_ON : ESMART_OFF; + config->hide_edge_borders = parse_boolean(argv[0], true) ? + E_SMART : config->saved_edge_borders; } + config->saved_edge_borders = saved; arrange_root(); diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index a6d165dc3..b27f9ccde 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c @@ -15,12 +15,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { return error; } - if (strcmp(argv[0], "inverse_outer") == 0) { - config->smart_gaps = SMART_GAPS_INVERSE_OUTER; - } else { - config->smart_gaps = parse_boolean(argv[0], config->smart_gaps) - ? SMART_GAPS_ON : SMART_GAPS_OFF; - } + config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); arrange_root(); diff --git a/sway/commands/split.c b/sway/commands/split.c index 500a497db..e96707228 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -13,8 +13,7 @@ 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_or_child(con) && - con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { + if (container_is_scratchpad_hidden(con)) { return cmd_results_new(CMD_FAILURE, "Cannot split a hidden scratchpad container"); } @@ -23,23 +22,8 @@ static struct cmd_results *do_split(int layout) { workspace_split(ws, layout); } - if (root->fullscreen_global) { - arrange_root(); - } else { - arrange_workspace(ws); - } - - return cmd_results_new(CMD_SUCCESS, NULL); -} - -static struct cmd_results *do_unsplit(void) { - struct sway_container *con = config->handler_context.container; - struct sway_workspace *ws = config->handler_context.workspace; - - if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) { - container_flatten(con->pending.parent); - } else { - return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings"); + if (con && con->parent && con->parent->parent) { + container_flatten(con->parent->parent); } if (root->fullscreen_global) { @@ -47,6 +31,7 @@ static struct cmd_results *do_unsplit(void) { } else { arrange_workspace(ws); } + return cmd_results_new(CMD_SUCCESS, NULL); } @@ -73,9 +58,6 @@ struct cmd_results *cmd_split(int argc, char **argv) { } else { return do_split(L_VERT); } - } else if (strcasecmp(argv[0], "n") == 0 || - strcasecmp(argv[0], "none") == 0) { - return do_unsplit(); } else { return cmd_results_new(CMD_FAILURE, "Invalid split command (expected either horizontal or vertical)."); @@ -93,7 +75,7 @@ struct cmd_results *cmd_splitv(int argc, char **argv) { struct cmd_results *cmd_splith(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "splith", EXPECTED_EQUAL_TO, 0))) { + if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) { return error; } return do_split(L_HORIZ); @@ -101,7 +83,7 @@ struct cmd_results *cmd_splith(int argc, char **argv) { struct cmd_results *cmd_splitt(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "splitt", EXPECTED_EQUAL_TO, 0))) { + if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) { return error; } diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 9b09a0f9d..5b70199cb 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c @@ -9,7 +9,6 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" -#include "log.h" #include "util.h" struct cmd_results *cmd_sticky(int argc, char **argv) { @@ -25,18 +24,13 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { container->is_sticky = parse_boolean(argv[0], container->is_sticky); - if (container_is_sticky_or_child(container) && + if (container->is_sticky && container_is_floating_or_child(container) && !container_is_scratchpad_hidden(container)) { // move container to active workspace struct sway_workspace *active_workspace = - output_get_active_workspace(container->pending.workspace->output); - if (!sway_assert(active_workspace, - "Expected output to have a workspace")) { - return cmd_results_new(CMD_FAILURE, - "Expected output to have a workspace"); - } - if (container->pending.workspace != active_workspace) { - struct sway_workspace *old_workspace = container->pending.workspace; + output_get_active_workspace(container->workspace->output); + if (container->workspace != active_workspace) { + struct sway_workspace *old_workspace = container->workspace; container_detach(container); workspace_add_floating(active_workspace, container); container_handle_fullscreen_reparent(container); diff --git a/sway/commands/swap.c b/sway/commands/swap.c index c0b0d0b9c..0e2c2d105 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,10 +1,10 @@ +#define _POSIX_C_SOURCE 200809L #include #include "config.h" #include "log.h" #include "sway/commands.h" #include "sway/output.h" #include "sway/tree/arrange.h" -#include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -13,12 +13,147 @@ static const char expected_syntax[] = "Expected 'swap container with id|con_id|mark '"; +static void swap_places(struct sway_container *con1, + struct sway_container *con2) { + struct sway_container *temp = malloc(sizeof(struct sway_container)); + temp->x = con1->x; + temp->y = con1->y; + temp->width = con1->width; + temp->height = con1->height; + temp->parent = con1->parent; + temp->workspace = con1->workspace; + + con1->x = con2->x; + con1->y = con2->y; + con1->width = con2->width; + con1->height = con2->height; + + con2->x = temp->x; + con2->y = temp->y; + con2->width = temp->width; + con2->height = temp->height; + + int temp_index = container_sibling_index(con1); + if (con2->parent) { + container_insert_child(con2->parent, con1, + container_sibling_index(con2)); + } else { + workspace_insert_tiling(con2->workspace, con1, + container_sibling_index(con2)); + } + if (temp->parent) { + container_insert_child(temp->parent, con2, temp_index); + } else { + workspace_insert_tiling(temp->workspace, con2, temp_index); + } + + free(temp); +} + +static void swap_focus(struct sway_container *con1, + struct sway_container *con2, struct sway_seat *seat, + struct sway_container *focus) { + if (focus == con1 || focus == con2) { + struct sway_workspace *ws1 = con1->workspace; + struct sway_workspace *ws2 = con2->workspace; + enum sway_container_layout layout1 = container_parent_layout(con1); + enum sway_container_layout layout2 = container_parent_layout(con2); + if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { + if (workspace_is_visible(ws2)) { + seat_set_focus(seat, &con2->node); + } + seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); + } else if (focus == con2 && (layout1 == L_TABBED + || layout1 == L_STACKED)) { + if (workspace_is_visible(ws1)) { + seat_set_focus(seat, &con1->node); + } + seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); + } else if (ws1 != ws2) { + seat_set_focus_container(seat, focus == con1 ? con2 : con1); + } else { + seat_set_focus_container(seat, focus); + } + } else { + seat_set_focus_container(seat, focus); + } + + if (root->fullscreen_global) { + seat_set_focus(seat, + seat_get_focus_inactive(seat, &root->fullscreen_global->node)); + } +} + +static void container_swap(struct sway_container *con1, + struct sway_container *con2) { + if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { + return; + } + if (!sway_assert(!container_has_ancestor(con1, con2) + && !container_has_ancestor(con2, con1), + "Cannot swap ancestor and descendant")) { + return; + } + if (!sway_assert(!container_is_floating(con1) + && !container_is_floating(con2), + "Swapping with floating containers is not supported")) { + return; + } + + sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", + con1->node.id, con2->node.id); + + enum sway_fullscreen_mode fs1 = con1->fullscreen_mode; + enum sway_fullscreen_mode fs2 = con2->fullscreen_mode; + if (fs1) { + container_fullscreen_disable(con1); + } + if (fs2) { + container_fullscreen_disable(con2); + } + + struct sway_seat *seat = config->handler_context.seat; + struct sway_container *focus = seat_get_focused_container(seat); + struct sway_workspace *vis1 = + output_get_active_workspace(con1->workspace->output); + struct sway_workspace *vis2 = + output_get_active_workspace(con2->workspace->output); + + char *stored_prev_name = NULL; + if (seat->prev_workspace_name) { + stored_prev_name = strdup(seat->prev_workspace_name); + } + + swap_places(con1, con2); + + if (!workspace_is_visible(vis1)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); + } + if (!workspace_is_visible(vis2)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); + } + + swap_focus(con1, con2, seat, focus); + + if (stored_prev_name) { + free(seat->prev_workspace_name); + seat->prev_workspace_name = stored_prev_name; + } + + if (fs1) { + container_set_fullscreen(con2, fs1); + } + if (fs2) { + container_set_fullscreen(con1, fs2); + } +} + static bool test_con_id(struct sway_container *container, void *data) { size_t *con_id = data; return container->node.id == *con_id; } -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND static bool test_id(struct sway_container *container, void *data) { xcb_window_t *wid = data; return (container->view && container->view->type == SWAY_VIEW_XWAYLAND @@ -28,8 +163,8 @@ static bool test_id(struct sway_container *container, void *data) { static bool test_mark(struct sway_container *container, void *mark) { if (container->marks->length) { - return list_seq_find(container->marks, - (int (*)(const void *, const void *))strcmp, mark) != -1; + return !list_seq_find(container->marks, + (int (*)(const void *, const void *))strcmp, mark); } return false; } @@ -45,7 +180,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { } if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } struct sway_container *current = config->handler_context.container; @@ -53,7 +188,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { char *value = join_args(argv + 3, argc - 3); if (strcasecmp(argv[2], "id") == 0) { -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); other = root_find_container(test_id, &id); #endif @@ -64,7 +199,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { other = root_find_container(test_mark, value); } else { free(value); - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); + return cmd_results_new(CMD_INVALID, expected_syntax); } if (!other) { @@ -73,13 +208,13 @@ struct cmd_results *cmd_swap(int argc, char **argv) { } else if (!current) { error = cmd_results_new(CMD_FAILURE, "Can only swap with containers and views"); - } else if (current == other) { - error = cmd_results_new(CMD_FAILURE, - "Cannot swap a container with itself"); } else if (container_has_ancestor(current, other) || container_has_ancestor(other, current)) { error = cmd_results_new(CMD_FAILURE, "Cannot swap ancestor and descendant"); + } else if (container_is_floating(current) || container_is_floating(other)) { + error = cmd_results_new(CMD_FAILURE, + "Swapping with floating containers is not supported"); } free(value); @@ -93,13 +228,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) { if (root->fullscreen_global) { arrange_root(); } else { - struct sway_node *current_parent = node_get_parent(¤t->node); - struct sway_node *other_parent = node_get_parent(&other->node); - if (current_parent) { - arrange_node(current_parent); - } - if (other_parent && current_parent != other_parent) { - arrange_node(other_parent); + arrange_node(node_get_parent(¤t->node)); + if (node_get_parent(&other->node) != node_get_parent(¤t->node)) { + arrange_node(node_get_parent(&other->node)); } } diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c index be298a290..c30355deb 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c @@ -4,10 +4,6 @@ #include "sway/tree/container.h" #include "sway/tree/root.h" -static void arrange_title_bar_iterator(struct sway_container *con, void *data) { - container_arrange_title_bar(con); -} - struct cmd_results *cmd_title_align(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { @@ -25,7 +21,10 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { "Expected 'title_align left|center|right'"); } - root_for_each_container(arrange_title_bar_iterator, NULL); + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole(output); + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index fbade4731..9d312470e 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c @@ -12,19 +12,17 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { return error; } struct sway_container *container = config->handler_context.container; - if (!container) { + if (!container || !container->view) { return cmd_results_new(CMD_INVALID, - "Only valid containers can have a title_format"); + "Only views can have a title_format"); } + struct sway_view *view = container->view; char *format = join_args(argv, argc); - if (container->title_format) { - free(container->title_format); - } - container->title_format = format; - if (container->view) { - view_update_title(container->view, true); - } else { - container_update_representation(container); + if (view->title_format) { + free(view->title_format); } + view->title_format = format; + view_update_title(view, true); + config_update_font_height(true); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index fa3db3c50..3c5e9ba12 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c @@ -21,12 +21,8 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - struct sway_workspace *ws = output_get_active_workspace(output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return cmd_results_new(CMD_FAILURE, - "Expected output to have a workspace"); - } - arrange_workspace(ws); + arrange_workspace(output_get_active_workspace(output)); + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c index 6999f7a27..29ce59ff0 100644 --- a/sway/commands/titlebar_padding.c +++ b/sway/commands/titlebar_padding.c @@ -33,6 +33,7 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; arrange_workspace(output_get_active_workspace(output)); + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index 4aba5bae0..cedfcfb2c 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -7,13 +8,9 @@ #include "log.h" #include "stringop.h" -static void remove_mark(struct sway_container *con) { - container_clear_marks(con); - container_update_marks(con); -} - static void remove_all_marks_iterator(struct sway_container *con, void *data) { - remove_mark(con); + container_clear_marks(con); + container_update_marks_textures(con); } // unmark Remove all marks from all views @@ -24,7 +21,7 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) { struct cmd_results *cmd_unmark(int argc, char **argv) { // Determine the container struct sway_container *con = NULL; - if (config->handler_context.node_overridden) { + if (config->handler_context.using_criteria) { con = config->handler_context.container; } @@ -41,7 +38,8 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { } } else if (con && !mark) { // Clear all marks from the given container - remove_mark(con); + container_clear_marks(con); + container_update_marks_textures(con); } else if (!con && mark) { // Remove mark from whichever container has it container_find_and_unmark(mark); diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index a14ebb209..65a3f4075 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -59,9 +60,6 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, int gaps_location) { const char expected[] = "Expected 'workspace gaps " "inner|outer|horizontal|vertical|top|right|bottom|left '"; - if (gaps_location == 0) { - return cmd_results_new(CMD_INVALID, "%s", expected); - } struct cmd_results *error = NULL; if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) { @@ -78,7 +76,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, char *end; int amount = strtol(argv[gaps_location + 2], &end, 10); if (strlen(end)) { - return cmd_results_new(CMD_FAILURE, "%s", expected); + return cmd_results_new(CMD_FAILURE, expected); } bool valid = false; @@ -109,7 +107,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, } } if (!valid) { - return cmd_results_new(CMD_INVALID, "%s", expected); + return cmd_results_new(CMD_INVALID, expected); } // Prevent invalid gaps configurations. @@ -142,10 +140,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { break; } } - if (output_location == 0) { - return cmd_results_new(CMD_INVALID, - "Expected 'workspace output '"); - } else if (output_location > 0) { + if (output_location >= 0) { if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, output_location + 2))) { return error; @@ -173,20 +168,19 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } if (root->fullscreen_global) { - return cmd_results_new(CMD_FAILURE, + return cmd_results_new(CMD_FAILURE, "workspace", "Can't switch workspaces while fullscreen global"); } - bool auto_back_and_forth = true; + bool no_auto_back_and_forth = false; while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { - auto_back_and_forth = false; + no_auto_back_and_forth = true; if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { return error; } ++argv; } - struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { @@ -203,9 +197,6 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { ws = workspace_create(NULL, name); free(name); } - if (ws && auto_back_and_forth) { - ws = workspace_auto_back_and_forth(ws); - } } else if (strcasecmp(argv[0], "next") == 0 || strcasecmp(argv[0], "prev") == 0 || strcasecmp(argv[0], "next_on_output") == 0 || @@ -213,6 +204,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { strcasecmp(argv[0], "current") == 0) { ws = workspace_by_name(argv[0]); } else if (strcasecmp(argv[0], "back_and_forth") == 0) { + struct sway_seat *seat = config->handler_context.seat; if (!seat->prev_workspace_name) { return cmd_results_new(CMD_INVALID, "There is no previous workspace"); @@ -226,15 +218,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { ws = workspace_create(NULL, name); } free(name); - if (ws && auto_back_and_forth) { - ws = workspace_auto_back_and_forth(ws); - } } - if (!ws) { - return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); - } - workspace_switch(ws); - seat_consider_warp_to_focus(seat); + workspace_switch(ws, no_auto_back_and_forth); + seat_consider_warp_to_focus(config->handler_context.seat); } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/ws_auto_back_and_forth.c b/sway/commands/ws_auto_back_and_forth.c index 5827c389d..e4411c8ed 100644 --- a/sway/commands/ws_auto_back_and_forth.c +++ b/sway/commands/ws_auto_back_and_forth.c @@ -8,7 +8,7 @@ struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) { if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) { return error; } - config->auto_back_and_forth = + config->auto_back_and_forth = parse_boolean(argv[0], config->auto_back_and_forth); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c index c0b175fcf..b72f3239d 100644 --- a/sway/commands/xwayland.c +++ b/sway/commands/xwayland.c @@ -10,23 +10,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) { return error; } -#ifdef WLR_HAS_XWAYLAND - enum xwayland_mode xwayland; - if (strcmp(argv[0], "force") == 0) { - xwayland = XWAYLAND_MODE_IMMEDIATE; - } else if (parse_boolean(argv[0], true)) { - xwayland = XWAYLAND_MODE_LAZY; - } else { - xwayland = XWAYLAND_MODE_DISABLED; - } - - // config->xwayland is reset to the previous value on reload in - // load_main_config() - if (config->reloading && config->xwayland != xwayland) { - return cmd_results_new(CMD_FAILURE, - "xwayland can only be enabled/disabled at launch"); - } - config->xwayland = xwayland; +#ifdef HAVE_XWAYLAND + config->xwayland = parse_boolean(argv[0], config->xwayland); #else sway_log(SWAY_INFO, "Ignoring `xwayland` command, " "sway hasn't been built with Xwayland support"); diff --git a/sway/config.c b/sway/config.c index d579022d3..206ca95cb 100644 --- a/sway/config.c +++ b/sway/config.c @@ -1,4 +1,3 @@ -#undef _POSIX_C_SOURCE #define _XOPEN_SOURCE 700 // for realpath #include #include @@ -18,50 +17,22 @@ #include #include "sway/input/input-manager.h" #include "sway/input/seat.h" -#include "sway/input/switch.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/criteria.h" #include "sway/desktop/transaction.h" -#include "sway/server.h" #include "sway/swaynag.h" #include "sway/tree/arrange.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" -#include "cairo_util.h" +#include "cairo.h" #include "pango.h" #include "stringop.h" #include "list.h" #include "log.h" -#include "util.h" struct sway_config *config = NULL; -static struct xkb_state *keysym_translation_state_create( - struct xkb_rule_names rules, uint32_t context_flags) { - struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV); - struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( - context, - &rules, - XKB_KEYMAP_COMPILE_NO_FLAGS); - xkb_context_unref(context); - if (xkb_keymap == NULL) { - sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap"); - return NULL; - } - - return xkb_state_new(xkb_keymap); -} - -static void keysym_translation_state_destroy( - struct xkb_state *state) { - if (state == NULL) { - return; - } - xkb_keymap_unref(xkb_state_get_keymap(state)); - xkb_state_unref(state); -} - static void free_mode(struct sway_mode *mode) { if (!mode) { return; @@ -85,18 +56,6 @@ 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); - } - if (mode->gesture_bindings) { - for (int i = 0; i < mode->gesture_bindings->length; i++) { - free_gesture_binding(mode->gesture_bindings->items[i]); - } - list_free(mode->gesture_bindings); - } free(mode); } @@ -139,21 +98,12 @@ 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]); @@ -169,6 +119,9 @@ void free_config(struct sway_config *config) { list_free(config->no_focus); list_free(config->active_bar_modifiers); list_free_items_and_destroy(config->config_chain); + list_free(config->command_policies); + list_free(config->feature_policies); + list_free(config->ipc_policies); free(config->floating_scroll_up_cmd); free(config->floating_scroll_down_cmd); free(config->floating_scroll_left_cmd); @@ -178,7 +131,6 @@ 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); } @@ -205,15 +157,24 @@ static void destroy_removed_seats(struct sway_config *old_config, } } +static void set_color(float dest[static 4], uint32_t color) { + dest[0] = ((color >> 16) & 0xff) / 255.0; + dest[1] = ((color >> 8) & 0xff) / 255.0; + dest[2] = (color & 0xff) / 255.0; + dest[3] = 1.0; +} + static void config_defaults(struct sway_config *config) { if (!(config->swaynag_command = strdup("swaynag"))) goto cleanup; - config->swaynag_config_errors = (struct swaynag_instance){0}; - config->swaynag_config_errors.args = "--type error " + config->swaynag_config_errors = (struct swaynag_instance){ + .args = "--type error " "--message 'There are errors in your config file' " "--detailed-message " - "--button-no-terminal 'Exit sway' 'swaymsg exit' " - "--button-no-terminal 'Reload sway' 'swaymsg reload'"; - config->swaynag_config_errors.detailed = true; + "--button 'Exit sway' 'swaymsg exit' " + "--button 'Reload sway' 'swaymsg reload'", + .pid = -1, + .detailed = true, + }; if (!(config->symbols = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup; @@ -221,12 +182,10 @@ 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)))) @@ -236,8 +195,6 @@ 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; - if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup; list_add(config->modes, config->current_mode); config->floating_mod = 0; @@ -252,11 +209,10 @@ static void config_defaults(struct sway_config *config) { config->default_layout = L_NONE; config->default_orientation = L_NONE; if (!(config->font = strdup("monospace 10"))) goto cleanup; - config->font_description = pango_font_description_from_string(config->font); + config->font_height = 17; // height of monospace 10 config->urgent_timeout = 500; - config->focus_on_window_activation = FOWA_URGENT; config->popup_during_fullscreen = POPUP_SMART; - config->xwayland = XWAYLAND_MODE_LAZY; + config->xwayland = true; config->titlebar_border_thickness = 1; config->titlebar_h_padding = 5; @@ -282,9 +238,8 @@ static void config_defaults(struct sway_config *config) { config->title_align = ALIGN_LEFT; config->tiling_drag = true; config->tiling_drag_threshold = 9; - config->primary_selection = true; - config->smart_gaps = SMART_GAPS_OFF; + config->smart_gaps = false; config->gaps_inner = 0; config->gaps_outer.top = 0; config->gaps_outer.right = 0; @@ -305,54 +260,45 @@ static void config_defaults(struct sway_config *config) { config->border_thickness = 2; config->floating_border_thickness = 2; config->hide_edge_borders = E_NONE; - config->hide_edge_borders_smart = ESMART_OFF; - config->hide_lone_tab = false; - - config->has_focused_tab_title = false; + config->saved_edge_borders = E_NONE; // border colors - color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); - color_to_rgba(config->border_colors.focused.background, 0x285577FF); - color_to_rgba(config->border_colors.focused.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.focused.indicator, 0x2E9EF4FF); - color_to_rgba(config->border_colors.focused.child_border, 0x285577FF); + set_color(config->border_colors.focused.border, 0x4C7899); + set_color(config->border_colors.focused.background, 0x285577); + set_color(config->border_colors.focused.text, 0xFFFFFFFF); + set_color(config->border_colors.focused.indicator, 0x2E9EF4); + set_color(config->border_colors.focused.child_border, 0x285577); - color_to_rgba(config->border_colors.focused_inactive.border, 0x333333FF); - color_to_rgba(config->border_colors.focused_inactive.background, 0x5F676AFF); - color_to_rgba(config->border_colors.focused_inactive.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.focused_inactive.indicator, 0x484E50FF); - color_to_rgba(config->border_colors.focused_inactive.child_border, 0x5F676AFF); + set_color(config->border_colors.focused_inactive.border, 0x333333); + set_color(config->border_colors.focused_inactive.background, 0x5F676A); + set_color(config->border_colors.focused_inactive.text, 0xFFFFFFFF); + set_color(config->border_colors.focused_inactive.indicator, 0x484E50); + set_color(config->border_colors.focused_inactive.child_border, 0x5F676A); - color_to_rgba(config->border_colors.unfocused.border, 0x333333FF); - color_to_rgba(config->border_colors.unfocused.background, 0x222222FF); - color_to_rgba(config->border_colors.unfocused.text, 0x888888FF); - color_to_rgba(config->border_colors.unfocused.indicator, 0x292D2EFF); - color_to_rgba(config->border_colors.unfocused.child_border, 0x222222FF); + set_color(config->border_colors.unfocused.border, 0x333333); + set_color(config->border_colors.unfocused.background, 0x222222); + set_color(config->border_colors.unfocused.text, 0x88888888); + set_color(config->border_colors.unfocused.indicator, 0x292D2E); + set_color(config->border_colors.unfocused.child_border, 0x222222); - color_to_rgba(config->border_colors.urgent.border, 0x2F343AFF); - color_to_rgba(config->border_colors.urgent.background, 0x900000FF); - color_to_rgba(config->border_colors.urgent.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.urgent.indicator, 0x900000FF); - color_to_rgba(config->border_colors.urgent.child_border, 0x900000FF); + set_color(config->border_colors.urgent.border, 0x2F343A); + set_color(config->border_colors.urgent.background, 0x900000); + set_color(config->border_colors.urgent.text, 0xFFFFFFFF); + set_color(config->border_colors.urgent.indicator, 0x900000); + set_color(config->border_colors.urgent.child_border, 0x900000); - color_to_rgba(config->border_colors.placeholder.border, 0x000000FF); - color_to_rgba(config->border_colors.placeholder.background, 0x0C0C0CFF); - color_to_rgba(config->border_colors.placeholder.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.placeholder.indicator, 0x000000FF); - color_to_rgba(config->border_colors.placeholder.child_border, 0x0C0C0CFF); + set_color(config->border_colors.placeholder.border, 0x000000); + set_color(config->border_colors.placeholder.background, 0x0C0C0C); + set_color(config->border_colors.placeholder.text, 0xFFFFFFFF); + set_color(config->border_colors.placeholder.indicator, 0x000000); + set_color(config->border_colors.placeholder.child_border, 0x0C0C0C); - color_to_rgba(config->border_colors.background, 0xFFFFFFFF); + set_color(config->border_colors.background, 0xFFFFFF); - // The keysym to keycode translation - struct xkb_rule_names rules = {0}; - config->keysym_translation_state = keysym_translation_state_create(rules, 0); - if (config->keysym_translation_state == NULL) { - config->keysym_translation_state = keysym_translation_state_create(rules, - XKB_CONTEXT_NO_ENVIRONMENT_NAMES); - } - if (config->keysym_translation_state == NULL) { - goto cleanup; - } + // Security + if (!(config->command_policies = create_list())) goto cleanup; + if (!(config->feature_policies = create_list())) goto cleanup; + if (!(config->ipc_policies = create_list())) goto cleanup; return; cleanup: @@ -363,53 +309,35 @@ static bool file_exists(const char *path) { return path && access(path, R_OK) != -1; } -static char *config_path(const char *prefix, const char *config_folder) { - if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { - return NULL; - } - return format_str("%s/%s/config", prefix, config_folder); -} - static char *get_config_path(void) { - char *path = NULL; - const char *home = getenv("HOME"); - char *config_home_fallback = NULL; - - const char *config_home = getenv("XDG_CONFIG_HOME"); - if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { - config_home_fallback = format_str("%s/.config", home); - config_home = config_home_fallback; - } - - struct config_path { - const char *prefix; - const char *config_folder; + static const char *config_paths[] = { + "$HOME/.sway/config", + "$XDG_CONFIG_HOME/sway/config", + "$HOME/.i3/config", + "$XDG_CONFIG_HOME/i3/config", + SYSCONFDIR "/sway/config", + SYSCONFDIR "/i3/config", }; - struct config_path config_paths[] = { - { .prefix = home, .config_folder = ".sway"}, - { .prefix = config_home, .config_folder = "sway"}, - { .prefix = home, .config_folder = ".i3"}, - { .prefix = config_home, .config_folder = "i3"}, - { .prefix = SYSCONFDIR, .config_folder = "sway"}, - { .prefix = SYSCONFDIR, .config_folder = "i3"} - }; - - size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]); - for (size_t i = 0; i < num_config_paths; i++) { - path = config_path(config_paths[i].prefix, config_paths[i].config_folder); - if (!path) { - continue; - } - if (file_exists(path)) { - break; - } - free(path); - path = NULL; + char *config_home = getenv("XDG_CONFIG_HOME"); + if (!config_home || !*config_home) { + config_paths[1] = "$HOME/.config/sway/config"; + config_paths[3] = "$HOME/.config/i3/config"; } - free(config_home_fallback); - return path; + for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { + wordexp_t p; + if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { + char *path = strdup(p.we_wordv[0]); + wordfree(&p); + if (file_exists(path)) { + return path; + } + free(path); + } + } + + return NULL; } static bool load_config(const char *path, struct sway_config *config, @@ -440,7 +368,7 @@ static bool load_config(const char *path, struct sway_config *config, sway_log(SWAY_ERROR, "Error(s) loading config!"); } - return config->active || !config->validating || config_load_success; + return true; } bool load_main_config(const char *file, bool is_active, bool validating) { @@ -450,14 +378,10 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } else { path = get_config_path(); } - if (path == NULL) { - sway_log(SWAY_ERROR, "Cannot find config file"); - return false; - } char *real_path = realpath(path, NULL); if (real_path == NULL) { - sway_log(SWAY_ERROR, "%s not found", path); + sway_log(SWAY_DEBUG, "%s not found.", path); free(path); return false; } @@ -476,36 +400,69 @@ bool load_main_config(const char *file, bool is_active, bool validating) { config->reloading = true; config->active = true; - // xwayland can only be enabled/disabled at launch - sway_log(SWAY_DEBUG, "xwayland will remain %s", - old_config->xwayland ? "enabled" : "disabled"); - config->xwayland = old_config->xwayland; + swaynag_kill(&old_config->swaynag_config_errors); + memcpy(&config->swaynag_config_errors, + &old_config->swaynag_config_errors, + sizeof(struct swaynag_instance)); - // primary_selection can only be enabled/disabled at launch - sway_log(SWAY_DEBUG, "primary_selection will remain %s", - old_config->primary_selection ? "enabled" : "disabled"); - config->primary_selection = old_config->primary_selection; - - if (!config->validating) { - 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(); - } + input_manager_reset_all_inputs(); } - config->user_config_path = file ? true : false; config->current_config_path = path; list_add(config->config_chain, real_path); config->reading = true; - bool success = load_config(path, config, &config->swaynag_config_errors); + // Read security configs + // TODO: Security + bool success = true; + /* + DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); + if (!dir) { + sway_log(SWAY_ERROR, + "%s does not exist, sway will have no security configuration" + " and will probably be broken", SYSCONFDIR "/sway/security.d"); + } else { + list_t *secconfigs = create_list(); + char *base = SYSCONFDIR "/sway/security.d/"; + struct dirent *ent = readdir(dir); + struct stat s; + while (ent != NULL) { + char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); + strcpy(_path, base); + strcat(_path, ent->d_name); + lstat(_path, &s); + if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { + list_add(secconfigs, _path); + } + else { + free(_path); + } + ent = readdir(dir); + } + closedir(dir); + + list_qsort(secconfigs, qstrcmp); + for (int i = 0; i < secconfigs->length; ++i) { + char *_path = secconfigs->items[i]; + if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || + (((s.st_mode & 0777) != 0644) && + (s.st_mode & 0777) != 0444)) { + sway_log(SWAY_ERROR, + "Refusing to load %s - it must be owned by root " + "and mode 644 or 444", _path); + success = false; + } else { + success = success && load_config(_path, config); + } + } + + list_free_items_and_destroy(secconfigs); + } + */ + + success = success && load_config(path, config, + &config->swaynag_config_errors); if (validating) { free_config(config); @@ -513,34 +470,17 @@ bool load_main_config(const char *file, bool is_active, bool validating) { return success; } - // Only really necessary if not explicitly `font` is set in the config. - config_update_font_height(); - - if (!validating) { - input_manager_verify_fallback_seat(); - - for (int i = 0; i < config->input_configs->length; i++) { - input_manager_apply_input_config(config->input_configs->items[i]); - } - - for (int i = 0; i < config->input_type_configs->length; i++) { - input_manager_apply_input_config( - config->input_type_configs->items[i]); - } - - for (int i = 0; i < config->seat_configs->length; i++) { - input_manager_apply_seat_config(config->seat_configs->items[i]); - } - sway_switch_retrigger_bindings_for_all(); - - spawn_swaybg(); + if (is_active) { + reset_outputs(); config->reloading = false; - if (is_active) { - request_modeset(); - if (config->swaynag_config_errors.client != NULL) { - swaynag_show(&config->swaynag_config_errors); - } + if (config->swaynag_config_errors.pid > 0) { + swaynag_show(&config->swaynag_config_errors); + } + + input_manager_verify_fallback_seat(); + for (int i = 0; i < config->seat_configs->length; i++) { + input_manager_apply_seat_config(config->seat_configs->items[i]); } } @@ -552,12 +492,28 @@ bool load_main_config(const char *file, bool is_active, bool validating) { return success; } -static bool load_include_config(const char *path, struct sway_config *config, - struct swaynag_instance *swaynag) { +static bool load_include_config(const char *path, const char *parent_dir, + struct sway_config *config, struct swaynag_instance *swaynag) { // save parent config const char *parent_config = config->current_config_path; - char *real_path = realpath(path, NULL); + char *full_path; + int len = strlen(path); + if (len >= 1 && path[0] != '/') { + len = len + strlen(parent_dir) + 2; + full_path = malloc(len * sizeof(char)); + if (!full_path) { + sway_log(SWAY_ERROR, + "Unable to allocate full path to included config"); + return false; + } + snprintf(full_path, len, "%s/%s", parent_dir, path); + } else { + full_path = strdup(path); + } + + char *real_path = realpath(full_path, NULL); + free(full_path); if (real_path == NULL) { sway_log(SWAY_DEBUG, "%s not found.", path); @@ -593,34 +549,43 @@ static bool load_include_config(const char *path, struct sway_config *config, return true; } -void load_include_configs(const char *path, struct sway_config *config, +bool 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) { - sway_log(SWAY_ERROR, "failed to change working directory"); - goto cleanup; + free(parent_path); + free(wd); + return false; } wordexp_t p; - 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], config, swaynag); - } - wordfree(&p); + + if (wordexp(path, &p, 0) != 0) { + free(parent_path); + free(wd); + return false; } - // Attempt to restore working directory before returning. - if (chdir(wd) < 0) { - sway_log(SWAY_ERROR, "failed to change working directory"); + 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); } -cleanup: free(parent_path); + wordfree(&p); + + // restore wd + if (chdir(wd) < 0) { + free(wd); + sway_log(SWAY_ERROR, "failed to restore working directory"); + return false; + } + free(wd); + return true; } void run_deferred_commands(void) { @@ -643,23 +608,7 @@ void run_deferred_commands(void) { list_free(res_list); free(line); } -} - -void run_deferred_bindings(void) { - struct sway_seat *seat; - wl_list_for_each(seat, &(server.input->seats), link) { - if (!seat->deferred_bindings->length) { - continue; - } - sway_log(SWAY_DEBUG, "Running deferred bindings for seat %s", - seat->wlr_seat->name); - while (seat->deferred_bindings->length) { - struct sway_binding *binding = seat->deferred_bindings->items[0]; - seat_execute_command(seat, binding); - list_del(seat->deferred_bindings, 0); - free_sway_binding(binding); - } - } + transaction_commit_dirty(); } // get line, with backslash continuation @@ -669,7 +618,7 @@ static ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file, size_t next_line_size = 0; ssize_t nread = getline(lineptr, line_size, file); *nlines = nread == -1 ? 0 : 1; - while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0 && (*lineptr)[0] != '#') { + while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0) { ssize_t next_nread = getline(&next_line, &next_line_size, file); if (next_nread == -1) { break; @@ -679,10 +628,8 @@ static ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file, nread += next_nread - 2; if ((ssize_t) *line_size < nread + 1) { *line_size = nread + 1; - char *old_ptr = *lineptr; *lineptr = realloc(*lineptr, *line_size); if (!*lineptr) { - free(old_ptr); nread = -1; break; } @@ -872,18 +819,23 @@ void config_add_swaynag_warning(char *fmt, ...) { if (config->reading && !config->validating) { va_list args; va_start(args, fmt); - char *str = vformat_str(fmt, args); + size_t length = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); - if (str == NULL) { + + char *temp = malloc(length + 1); + if (!temp) { + sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); return; } + va_start(args, fmt); + vsnprintf(temp, length, fmt, args); + va_end(args); + swaynag_log(config->swaynag_command, &config->swaynag_config_errors, "Warning on line %i (%s) '%s': %s", config->current_config_line_number, config->current_config_path, - config->current_config_line, str); - - free(str); + config->current_config_line, temp); } } @@ -909,8 +861,8 @@ char *do_var_replacement(char *str) { // Find matching variable for (i = 0; i < config->symbols->length; ++i) { struct sway_variable *var = config->symbols->items[i]; - if (has_prefix(find, var->name)) { - int vnlen = strlen(var->name); + int vnlen = strlen(var->name); + if (strncmp(find, var->name, vnlen) == 0) { int vvlen = strlen(var->value); char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); if (!newstr) { @@ -923,7 +875,7 @@ char *do_var_replacement(char *str) { int offset = find - str; strncpy(newptr, str, offset); newptr += offset; - memcpy(newptr, var->value, vvlen); + strncpy(newptr, var->value, vvlen); newptr += vvlen; strcpy(newptr, find + vnlen); free(str); @@ -947,67 +899,33 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { return lenient_strcmp(wsa->workspace, wsb->workspace); } +static void find_font_height_iterator(struct sway_container *con, void *data) { + size_t amount_below_baseline = con->title_height - con->title_baseline; + size_t extended_height = config->font_baseline + amount_below_baseline; + if (extended_height > config->font_height) { + config->font_height = extended_height; + } +} -void config_update_font_height(void) { - int prev_max_height = config->font_height; +static void find_baseline_iterator(struct sway_container *con, void *data) { + bool *recalculate = data; + if (*recalculate) { + container_calculate_title_height(con); + } + if (con->title_baseline > config->font_baseline) { + config->font_baseline = con->title_baseline; + } +} - get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); +void config_update_font_height(bool recalculate) { + size_t prev_max_height = config->font_height; + config->font_height = 0; + config->font_baseline = 0; + + root_for_each_container(find_baseline_iterator, &recalculate); + root_for_each_container(find_font_height_iterator, NULL); if (config->font_height != prev_max_height) { 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, 0); - if (config->keysym_translation_state == NULL) { - sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state " - "for device '%s'", input_config->identifier); - return; - } - - 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 f4efb276c..2e28fa1ef 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -1,23 +1,33 @@ -#include -#include +#define _POSIX_C_SOURCE 200809L #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" -#include "sway/server.h" #include "config.h" #include "list.h" #include "log.h" #include "stringop.h" -#include "util.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); + } +} void free_bar_binding(struct bar_binding *binding) { if (!binding) { @@ -39,15 +49,13 @@ void free_bar_config(struct bar_config *bar) { free(bar->swaybar_command); free(bar->font); free(bar->separator_symbol); - if (bar->bindings) { - for (int i = 0; i < bar->bindings->length; i++) { - free_bar_binding(bar->bindings->items[i]); - } + for (int i = 0; i < bar->bindings->length; i++) { + free_bar_binding(bar->bindings->items[i]); } list_free(bar->bindings); list_free_items_and_destroy(bar->outputs); - if (bar->client != NULL) { - wl_client_destroy(bar->client); + if (bar->pid != 0) { + terminate_swaybar(bar->pid); } free(bar->colors.background); free(bar->colors.statusline); @@ -91,7 +99,7 @@ struct bar_config *default_bar_config(void) { } bar->outputs = NULL; bar->position = strdup("bottom"); - bar->pango_markup = PANGO_MARKUP_DEFAULT; + bar->pango_markup = false; bar->swaybar_command = NULL; bar->font = NULL; bar->height = 0; @@ -102,10 +110,10 @@ 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; - bar->workspace_min_width = 0; if (!(bar->mode = strdup("dock"))) { goto cleanup; } @@ -175,72 +183,70 @@ struct bar_config *default_bar_config(void) { wl_list_init(&bar->tray_bindings); #endif + list_add(config->bars, bar); return bar; cleanup: free_bar_config(bar); 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) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { - sway_log_errno(SWAY_ERROR, "socketpair failed"); - return; - } - if (!sway_set_cloexec(sockets[0], true) || !sway_set_cloexec(sockets[1], true)) { + // Pipe to communicate errors + int filedes[2]; + if (pipe(filedes) == -1) { + sway_log(SWAY_ERROR, "Pipe setup failed! Cannot fork into bar"); return; } - 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) { - if (!sway_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); + bar->pid = fork(); + if (bar->pid == 0) { + setpgid(0, 0); + close(filedes[0]); + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); // run custom swaybar - char *const cmd[] = { + size_t len = snprintf(NULL, 0, "%s -b %s", bar->swaybar_command ? bar->swaybar_command : "swaybar", - "-b", bar->id, NULL}; + 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); + } + 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(EXIT_FAILURE); + exit(1); } - - if (close(sockets[1]) != 0) { - sway_log_errno(SWAY_ERROR, "close failed"); - return; + 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); } - - sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); + close(filedes[1]); } void load_swaybar(struct bar_config *bar) { - if (bar->client != NULL) { - wl_client_destroy(bar->client); + if (bar->pid != 0) { + terminate_swaybar(bar->pid); } 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 1fb737b65..63c28635b 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -1,9 +1,8 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include #include "sway/config.h" -#include "sway/input/keyboard.h" -#include "sway/server.h" #include "log.h" struct input_config *new_input_config(const char* identifier) { @@ -19,32 +18,25 @@ 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; input->drag_lock = INT_MIN; input->dwt = INT_MIN; - input->dwtp = INT_MIN; input->send_events = INT_MIN; input->click_method = INT_MIN; - input->clickfinger_button_map = INT_MIN; input->middle_emulation = INT_MIN; input->natural_scroll = INT_MIN; input->accel_profile = INT_MIN; - input->rotation_angle = FLT_MIN; input->pointer_accel = FLT_MIN; input->scroll_factor = FLT_MIN; input->scroll_button = INT_MIN; - input->scroll_button_lock = INT_MIN; input->scroll_method = INT_MIN; input->left_handed = INT_MIN; input->repeat_delay = INT_MIN; input->repeat_rate = INT_MIN; input->xkb_numlock = INT_MIN; input->xkb_capslock = INT_MIN; - input->xkb_file_is_set = false; - input->tools = create_list(); return input; } @@ -56,9 +48,6 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->click_method != INT_MIN) { dst->click_method = src->click_method; } - if (src->clickfinger_button_map != INT_MIN) { - dst->clickfinger_button_map = src->clickfinger_button_map; - } if (src->drag != INT_MIN) { dst->drag = src->drag; } @@ -68,9 +57,6 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->dwt != INT_MIN) { dst->dwt = src->dwt; } - if (src->dwtp != INT_MIN) { - dst->dwtp = src->dwtp; - } if (src->left_handed != INT_MIN) { dst->left_handed = src->left_handed; } @@ -80,9 +66,6 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->natural_scroll != INT_MIN) { dst->natural_scroll = src->natural_scroll; } - if (src->rotation_angle != FLT_MIN) { - dst->rotation_angle = src->rotation_angle; - } if (src->pointer_accel != FLT_MIN) { dst->pointer_accel = src->pointer_accel; } @@ -101,9 +84,6 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->scroll_button != INT_MIN) { dst->scroll_button = src->scroll_button; } - if (src->scroll_button_lock != INT_MIN) { - dst->scroll_button_lock = src->scroll_button_lock; - } if (src->send_events != INT_MIN) { dst->send_events = src->send_events; } @@ -113,11 +93,6 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->tap_button_map != INT_MIN) { dst->tap_button_map = src->tap_button_map; } - if (src->xkb_file_is_set) { - free(dst->xkb_file); - dst->xkb_file = src->xkb_file ? strdup(src->xkb_file) : NULL; - dst->xkb_file_is_set = dst->xkb_file != NULL; - } if (src->xkb_layout) { free(dst->xkb_layout); dst->xkb_layout = strdup(src->xkb_layout); @@ -151,84 +126,10 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { memcpy(dst->mapped_from_region, src->mapped_from_region, sizeof(struct input_config_mapped_from_region)); } - if (src->mapped_to) { - dst->mapped_to = src->mapped_to; - } if (src->mapped_to_output) { free(dst->mapped_to_output); dst->mapped_to_output = strdup(src->mapped_to_output); } - if (src->mapped_to_region) { - free(dst->mapped_to_region); - dst->mapped_to_region = - malloc(sizeof(struct wlr_box)); - memcpy(dst->mapped_to_region, src->mapped_to_region, - sizeof(struct wlr_box)); - } - if (src->calibration_matrix.configured) { - dst->calibration_matrix.configured = src->calibration_matrix.configured; - memcpy(dst->calibration_matrix.matrix, src->calibration_matrix.matrix, - sizeof(src->calibration_matrix.matrix)); - } - for (int i = 0; i < src->tools->length; i++) { - struct input_config_tool *src_tool = src->tools->items[i]; - for (int j = 0; j < dst->tools->length; j++) { - struct input_config_tool *dst_tool = dst->tools->items[j]; - if (src_tool->type == dst_tool->type) { - dst_tool->mode = src_tool->mode; - goto tool_merge_outer; - } - } - - struct input_config_tool *dst_tool = malloc(sizeof(*dst_tool)); - memcpy(dst_tool, src_tool, sizeof(*dst_tool)); - list_add(dst->tools, dst_tool); - - tool_merge_outer:; - } -} - -static bool validate_xkb_merge(struct input_config *dest, - struct input_config *src, char **xkb_error) { - struct input_config *temp = new_input_config("temp"); - if (dest) { - merge_input_config(temp, dest); - } - merge_input_config(temp, src); - - struct xkb_keymap *keymap = sway_keyboard_compile_keymap(temp, xkb_error); - free_input_config(temp); - if (!keymap) { - return false; - } - - xkb_keymap_unref(keymap); - return true; -} - -static bool validate_wildcard_on_all(struct input_config *wildcard, - char **error) { - for (int i = 0; i < config->input_configs->length; i++) { - struct input_config *ic = config->input_configs->items[i]; - if (strcmp(wildcard->identifier, ic->identifier) != 0) { - sway_log(SWAY_DEBUG, "Validating xkb merge of * on %s", - ic->identifier); - if (!validate_xkb_merge(ic, wildcard, error)) { - return false; - } - } - } - - for (int i = 0; i < config->input_type_configs->length; i++) { - struct input_config *ic = config->input_type_configs->items[i]; - sway_log(SWAY_DEBUG, "Validating xkb merge of * config on %s", - ic->identifier); - if (!validate_xkb_merge(ic, wildcard, error)) { - return false; - } - } - - return true; } static void merge_wildcard_on_all(struct input_config *wildcard) { @@ -239,128 +140,38 @@ 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]; - sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier); - merge_input_config(ic, wildcard); - } } -static bool validate_type_on_existing(struct input_config *type_wildcard, - char **error) { - 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, "Validating merge of %s on %s", - type_wildcard->identifier, ic->identifier); - if (!validate_xkb_merge(ic, type_wildcard, error)) { - return false; - } - } - } - return true; -} - -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); - } - } -} - -static const char *set_input_type(struct input_config *ic) { - struct sway_input_device *input_device; - wl_list_for_each(input_device, &server.input->devices, link) { - if (strcmp(input_device->identifier, ic->identifier) == 0) { - ic->input_type = input_device_get_type(input_device); - break; - } - } - return ic->input_type; -} - -struct input_config *store_input_config(struct input_config *ic, - char **error) { +struct input_config *store_input_config(struct input_config *ic) { bool wildcard = strcmp(ic->identifier, "*") == 0; - if (wildcard && error && !validate_wildcard_on_all(ic, error)) { - return NULL; - } - - bool type = has_prefix(ic->identifier, "type:"); - if (type && error && !validate_type_on_existing(ic, error)) { - return NULL; - } - - list_t *config_list = type ? config->input_type_configs - : config->input_configs; - - struct input_config *current = NULL; - bool new_current = false; - - int i = list_seq_find(config_list, input_identifier_cmp, ic->identifier); - if (i >= 0) { - current = config_list->items[i]; - } - - if (!current && !wildcard && !type && set_input_type(ic)) { - for (i = 0; i < config->input_type_configs->length; i++) { - struct input_config *tc = config->input_type_configs->items[i]; - if (strcmp(ic->input_type, tc->identifier + 5) == 0) { - current = new_input_config(ic->identifier); - current->input_type = ic->input_type; - merge_input_config(current, tc); - new_current = true; - break; - } - } - } - - i = list_seq_find(config->input_configs, input_identifier_cmp, "*"); - if (!current && i >= 0) { - current = new_input_config(ic->identifier); - merge_input_config(current, config->input_configs->items[i]); - new_current = true; - } - - if (error && !validate_xkb_merge(current, ic, error)) { - if (new_current) { - free_input_config(current); - } - return NULL; - } - if (wildcard) { merge_wildcard_on_all(ic); } - if (type) { - merge_type_on_existing(ic); - } - - if (current) { + int i = list_seq_find(config->input_configs, 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]; merge_input_config(current, ic); free_input_config(ic); ic = current; - } - - ic->xkb_file_is_set = ic->xkb_file != NULL; - - if (!current || new_current) { - list_add(config_list, ic); + } else if (!wildcard) { + sway_log(SWAY_DEBUG, "Adding non-wildcard input config"); + i = list_seq_find(config->input_configs, input_identifier_cmp, "*"); + if (i >= 0) { + sway_log(SWAY_DEBUG, "Merging on top of input * config"); + struct input_config *current = new_input_config(ic->identifier); + merge_input_config(current, config->input_configs->items[i]); + merge_input_config(current, ic); + free_input_config(ic); + ic = current; + } + list_add(config->input_configs, ic); + } else { + // New wildcard config. Just add it + sway_log(SWAY_DEBUG, "Adding input * config"); + list_add(config->input_configs, ic); } sway_log(SWAY_DEBUG, "Config stored for input %s", ic->identifier); @@ -368,21 +179,11 @@ 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; } free(ic->identifier); - free(ic->xkb_file); free(ic->xkb_layout); free(ic->xkb_model); free(ic->xkb_options); @@ -390,8 +191,6 @@ void free_input_config(struct input_config *ic) { free(ic->xkb_variant); free(ic->mapped_from_region); free(ic->mapped_to_output); - free(ic->mapped_to_region); - list_free_items_and_destroy(ic->tools); free(ic); } diff --git a/sway/config/output.c b/sway/config/output.c index 6d6afdc25..513d03e0f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,55 +1,30 @@ +#define _POSIX_C_SOURCE 200809L #include -#include +#include #include #include #include #include #include -#include -#include -#include #include #include -#include -#include -#include "sway/config.h" -#include "sway/desktop/transaction.h" -#include "sway/input/cursor.h" -#include "sway/layers.h" -#include "sway/lock.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/tree/arrange.h" -#include "sway/tree/root.h" #include "log.h" -#include "util.h" +#include "sway/config.h" +#include "sway/output.h" +#include "sway/tree/root.h" -#if WLR_HAS_DRM_BACKEND -#include -#endif +int output_name_cmp(const void *item, const void *data) { + const struct output_config *output = item; + const char *name = data; + + return strcmp(output->name, name); +} void output_get_identifier(char *identifier, size_t len, struct sway_output *output) { struct wlr_output *wlr_output = output->wlr_output; - snprintf(identifier, len, "%s %s %s", - wlr_output->make ? wlr_output->make : "Unknown", - wlr_output->model ? wlr_output->model : "Unknown", - wlr_output->serial ? wlr_output->serial : "Unknown"); -} - -const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { - switch (scale_filter) { - case SCALE_FILTER_DEFAULT: - return "smart"; - case SCALE_FILTER_LINEAR: - return "linear"; - case SCALE_FILTER_NEAREST: - return "nearest"; - case SCALE_FILTER_SMART: - return "smart"; - } - sway_assert(false, "Unknown value for scale_filter."); - return NULL; + snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, + wlr_output->serial); } struct output_config *new_output_config(const char *name) { @@ -65,103 +40,13 @@ struct output_config *new_output_config(const char *name) { oc->enabled = -1; oc->width = oc->height = -1; oc->refresh_rate = -1; - oc->custom_mode = -1; - oc->drm_mode.type = -1; - oc->x = oc->y = INT_MAX; + oc->x = oc->y = -1; oc->scale = -1; - oc->scale_filter = SCALE_FILTER_DEFAULT; oc->transform = -1; - oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; - oc->max_render_time = -1; - oc->adaptive_sync = -1; - oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; - oc->color_profile = COLOR_PROFILE_DEFAULT; - oc->color_transform = NULL; - oc->power = -1; - oc->allow_tearing = -1; - oc->hdr = -1; return oc; } -// supersede_output_config clears all fields in dst that were set in src -static void supersede_output_config(struct output_config *dst, struct output_config *src) { - if (src->enabled != -1) { - dst->enabled = -1; - } - if (src->width != -1) { - dst->width = -1; - } - if (src->height != -1) { - dst->height = -1; - } - if (src->x != INT_MAX) { - dst->x = INT_MAX; - } - if (src->y != INT_MAX) { - dst->y = INT_MAX; - } - if (src->scale != -1) { - dst->scale = -1; - } - if (src->scale_filter != SCALE_FILTER_DEFAULT) { - dst->scale_filter = SCALE_FILTER_DEFAULT; - } - if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { - dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; - } - if (src->refresh_rate != -1) { - dst->refresh_rate = -1; - } - if (src->custom_mode != -1) { - dst->custom_mode = -1; - } - if (src->drm_mode.type != (uint32_t) -1) { - dst->drm_mode.type = -1; - } - if (src->transform != -1) { - dst->transform = -1; - } - if (src->max_render_time != -1) { - dst->max_render_time = -1; - } - if (src->adaptive_sync != -1) { - dst->adaptive_sync = -1; - } - if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { - dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; - } - if (src->color_profile != COLOR_PROFILE_DEFAULT) { - if (dst->color_transform) { - wlr_color_transform_unref(dst->color_transform); - dst->color_transform = NULL; - } - dst->color_profile = COLOR_PROFILE_DEFAULT; - } - if (src->background) { - free(dst->background); - dst->background = NULL; - } - if (src->background_option) { - free(dst->background_option); - dst->background_option = NULL; - } - if (src->background_fallback) { - free(dst->background_fallback); - dst->background_fallback = NULL; - } - if (src->power != -1) { - dst->power = -1; - } - if (src->allow_tearing != -1) { - dst->allow_tearing = -1; - } - if (src->hdr != -1) { - dst->hdr = -1; - } -} - -// merge_output_config sets all fields in dst that were set in src -static void merge_output_config(struct output_config *dst, struct output_config *src) { +void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->enabled != -1) { dst->enabled = src->enabled; } @@ -171,50 +56,21 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->height != -1) { dst->height = src->height; } - if (src->x != INT_MAX) { + if (src->x != -1) { dst->x = src->x; } - if (src->y != INT_MAX) { + if (src->y != -1) { dst->y = src->y; } if (src->scale != -1) { dst->scale = src->scale; } - if (src->scale_filter != SCALE_FILTER_DEFAULT) { - dst->scale_filter = src->scale_filter; - } - if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { - dst->subpixel = src->subpixel; - } if (src->refresh_rate != -1) { dst->refresh_rate = src->refresh_rate; } - if (src->custom_mode != -1) { - dst->custom_mode = src->custom_mode; - } - if (src->drm_mode.type != (uint32_t) -1) { - memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode)); - } if (src->transform != -1) { dst->transform = src->transform; } - if (src->max_render_time != -1) { - dst->max_render_time = src->max_render_time; - } - if (src->adaptive_sync != -1) { - dst->adaptive_sync = src->adaptive_sync; - } - if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { - dst->render_bit_depth = src->render_bit_depth; - } - if (src->color_profile != COLOR_PROFILE_DEFAULT) { - if (src->color_transform) { - wlr_color_transform_ref(src->color_transform); - } - wlr_color_transform_unref(dst->color_transform); - dst->color_profile = src->color_profile; - dst->color_transform = src->color_transform; - } if (src->background) { free(dst->background); dst->background = strdup(src->background); @@ -227,934 +83,376 @@ static void merge_output_config(struct output_config *dst, struct output_config free(dst->background_fallback); dst->background_fallback = strdup(src->background_fallback); } - if (src->power != -1) { - dst->power = src->power; - } - if (src->allow_tearing != -1) { - dst->allow_tearing = src->allow_tearing; - } - if (src->hdr != -1) { - dst->hdr = src->hdr; + if (src->dpms_state != 0) { + dst->dpms_state = src->dpms_state; } } -void store_output_config(struct output_config *oc) { - bool merged = false; - bool wildcard = strcmp(oc->name, "*") == 0; - struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name); +static void merge_wildcard_on_all(struct output_config *wildcard) { + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *oc = config->output_configs->items[i]; + if (strcmp(wildcard->name, oc->name) != 0) { + sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); + merge_output_config(oc, wildcard); + } + } +} - char id[128]; - if (output) { - output_get_identifier(id, sizeof(id), output); +struct output_config *store_output_config(struct output_config *oc) { + bool wildcard = strcmp(oc->name, "*") == 0; + if (wildcard) { + merge_wildcard_on_all(oc); } - for (int i = 0; i < config->output_configs->length; i++) { - struct output_config *old = config->output_configs->items[i]; - - // If the old config matches the new config's name, regardless of - // whether it was name or identifier, merge on top of the existing - // config. If the new config is a wildcard, this also merges on top of - // old wildcard configs. - if (strcmp(old->name, oc->name) == 0) { - merge_output_config(old, oc); - merged = true; - continue; - } - - // If the new config is a wildcard config we supersede all non-wildcard - // configs. Old wildcard configs have already been handled above. - if (wildcard) { - supersede_output_config(old, oc); - continue; - } - - // If the new config matches an output's name, and the old config - // matches on that output's identifier, supersede it. - if (output && strcmp(old->name, id) == 0 && - strcmp(oc->name, output->wlr_output->name) == 0) { - supersede_output_config(old, oc); + int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); + if (i >= 0) { + sway_log(SWAY_DEBUG, "Merging on top of existing output config"); + struct output_config *current = config->output_configs->items[i]; + merge_output_config(current, oc); + free_output_config(oc); + oc = current; + } else if (!wildcard) { + sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); + i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + sway_log(SWAY_DEBUG, "Merging on top of output * config"); + struct output_config *current = new_output_config(oc->name); + merge_output_config(current, config->output_configs->items[i]); + merge_output_config(current, oc); + free_output_config(oc); + oc = current; } + list_add(config->output_configs, oc); + } else { + // New wildcard config. Just add it + sway_log(SWAY_DEBUG, "Adding output * config"); + list_add(config->output_configs, oc); } sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " - "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " - "(max render time: %d) (allow tearing: %d) (hdr: %d)", + "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, - oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), - oc->transform, oc->background, oc->background_option, oc->power, - oc->max_render_time, oc->allow_tearing, oc->hdr); + oc->x, oc->y, oc->scale, oc->transform, oc->background, + oc->background_option, oc->dpms_state); - // If the configuration was not merged into an existing configuration, add - // it to the list. Otherwise we're done with it and can free it. - if (!merged) { - list_add(config->output_configs, oc); - } else { - free_output_config(oc); - } + return oc; } -static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, - int width, int height, float refresh_rate, bool custom) { - // Not all floating point integers can be represented exactly - // as (int)(1000 * mHz / 1000.f) - // round() the result to avoid any error - int mhz = (int)roundf(refresh_rate * 1000); - // If no target refresh rate is given, match highest available - mhz = mhz <= 0 ? INT_MAX : mhz; - - if (wl_list_empty(&output->modes) || custom) { - wlr_output_state_set_custom_mode(pending, width, height, - refresh_rate > 0 ? mhz : 0); - return; +static bool set_mode(struct wlr_output *output, int width, int height, + float refresh_rate) { + int mhz = (int)(refresh_rate * 1000); + if (wl_list_empty(&output->modes)) { + sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); + return wlr_output_set_custom_mode(output, width, height, mhz); } struct wlr_output_mode *mode, *best = NULL; - int best_diff_mhz = INT_MAX; wl_list_for_each(mode, &output->modes, link) { if (mode->width == width && mode->height == height) { - int diff_mhz = abs(mode->refresh - mhz); - if (diff_mhz < best_diff_mhz) { - best_diff_mhz = diff_mhz; + if (mode->refresh == mhz) { best = mode; - if (best_diff_mhz == 0) { - break; - } + break; } + best = mode; } } if (!best) { - best = wlr_output_preferred_mode(output); - sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " - "applying preferred mode (%dx%d@%.3fHz)", - width, height, refresh_rate, - best->width, best->height, best->refresh / 1000.f); - } - wlr_output_state_set_mode(pending, best); -} - -static void set_modeline(struct wlr_output *output, - struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { -#if WLR_HAS_DRM_BACKEND - if (!wlr_output_is_drm(output)) { - sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); - return; - } - struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); - if (mode) { - wlr_output_state_set_mode(pending, mode); - } -#else - sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); -#endif -} - -bool output_supports_hdr(struct wlr_output *output, const char **unsupported_reason_ptr) { - const char *unsupported_reason = NULL; - if (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020)) { - unsupported_reason = "BT2020 primaries not supported by output"; - } else if (!(output->supported_transfer_functions & WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) { - unsupported_reason = "PQ transfer function not supported by output"; - } else if (!server.renderer->features.output_color_transform) { - unsupported_reason = "renderer doesn't support output color transforms"; - } - if (unsupported_reason_ptr != NULL) { - *unsupported_reason_ptr = unsupported_reason; - } - return unsupported_reason == NULL; -} - -static void set_hdr(struct wlr_output *output, struct wlr_output_state *pending, bool enabled) { - const char *unsupported_reason = NULL; - if (enabled && !output_supports_hdr(output, &unsupported_reason)) { - sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: %s", - output->name, unsupported_reason); - enabled = false; - } - - if (!enabled) { - if (output->supported_primaries != 0 || output->supported_transfer_functions != 0) { - sway_log(SWAY_DEBUG, "Disabling HDR on output %s", output->name); - wlr_output_state_set_image_description(pending, NULL); - } - return; - } - - sway_log(SWAY_DEBUG, "Enabling HDR on output %s", output->name); - const struct wlr_output_image_description image_desc = { - .primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020, - .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ, - }; - wlr_output_state_set_image_description(pending, &image_desc); -} - -/* Some manufacturers hardcode the aspect-ratio of the output in the physical - * size field. */ -static bool phys_size_is_aspect_ratio(struct wlr_output *output) { - return (output->phys_width == 1600 && output->phys_height == 900) || - (output->phys_width == 1600 && output->phys_height == 1000) || - (output->phys_width == 160 && output->phys_height == 90) || - (output->phys_width == 160 && output->phys_height == 100) || - (output->phys_width == 16 && output->phys_height == 9) || - (output->phys_width == 16 && output->phys_height == 10); -} - -// The minimum DPI at which we turn on a scale of 2 -#define HIDPI_DPI_LIMIT (2 * 96) -// The minimum screen height at which we turn on a scale of 2 -#define HIDPI_MIN_HEIGHT 1200 -// 1 inch = 25.4 mm -#define MM_PER_INCH 25.4 - -static int compute_default_scale(struct wlr_output *output, - struct wlr_output_state *pending) { - struct wlr_box box = { .width = output->width, .height = output->height }; - if (pending->committed & WLR_OUTPUT_STATE_MODE) { - switch (pending->mode_type) { - case WLR_OUTPUT_STATE_MODE_FIXED: - box.width = pending->mode->width; - box.height = pending->mode->height; - break; - case WLR_OUTPUT_STATE_MODE_CUSTOM: - box.width = pending->custom_mode.width; - box.height = pending->custom_mode.height; - break; - } - } - enum wl_output_transform transform = output->transform; - if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { - transform = pending->transform; - } - wlr_box_transform(&box, &box, transform, box.width, box.height); - - int width = box.width; - int height = box.height; - - if (height < HIDPI_MIN_HEIGHT) { - return 1; - } - - if (output->phys_width == 0 || output->phys_height == 0) { - return 1; - } - - if (phys_size_is_aspect_ratio(output)) { - return 1; - } - - double dpi_x = (double) width / (output->phys_width / MM_PER_INCH); - double dpi_y = (double) height / (output->phys_height / MM_PER_INCH); - if (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) { - return 1; - } - - return 2; -} - -static enum render_bit_depth bit_depth_from_format(uint32_t render_format) { - if (render_format == DRM_FORMAT_XRGB2101010 || render_format == DRM_FORMAT_XBGR2101010) { - return RENDER_BIT_DEPTH_10; - } else if (render_format == DRM_FORMAT_XRGB8888 || render_format == DRM_FORMAT_ARGB8888) { - return RENDER_BIT_DEPTH_8; - } else if (render_format == DRM_FORMAT_RGB565) { - return RENDER_BIT_DEPTH_6; - } - return RENDER_BIT_DEPTH_DEFAULT; -} - -static enum render_bit_depth get_config_render_bit_depth(const struct output_config *oc) { - if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { - return oc->render_bit_depth; - } - if (oc && oc->hdr == 1) { - return RENDER_BIT_DEPTH_10; - } - return RENDER_BIT_DEPTH_8; -} - -static bool render_format_is_bgr(uint32_t fmt) { - return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888; -} - -static bool output_config_is_disabling(struct output_config *oc) { - return oc && (!oc->enabled || oc->power == 0); -} - -static void queue_output_config(struct output_config *oc, - struct sway_output *output, struct wlr_output_state *pending) { - if (output == root->fallback_output) { - return; - } - - struct wlr_output *wlr_output = output->wlr_output; - - if (output_config_is_disabling(oc)) { - wlr_output_state_set_enabled(pending, false); - return; - } - wlr_output_state_set_enabled(pending, true); - - if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { - set_modeline(wlr_output, pending, &oc->drm_mode); - } else if (oc && oc->width > 0 && oc->height > 0) { - set_mode(wlr_output, pending, oc->width, oc->height, - oc->refresh_rate, oc->custom_mode == 1); - } else if (!wl_list_empty(&wlr_output->modes)) { - struct wlr_output_mode *preferred_mode = - wlr_output_preferred_mode(wlr_output); - wlr_output_state_set_mode(pending, preferred_mode); - } - - if (oc && oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { - wlr_output_state_set_subpixel(pending, oc->subpixel); + sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); + sway_log(SWAY_INFO, "Picking default mode instead"); + best = wl_container_of(output->modes.prev, mode, link); } else { - wlr_output_state_set_subpixel(pending, output->detected_subpixel); + sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); } - - if (oc && oc->transform >= 0) { - wlr_output_state_set_transform(pending, oc->transform); -#if WLR_HAS_DRM_BACKEND - } else if (wlr_output_is_drm(wlr_output)) { - wlr_output_state_set_transform(pending, - wlr_drm_connector_get_panel_orientation(wlr_output)); -#endif - } else { - wlr_output_state_set_transform(pending, WL_OUTPUT_TRANSFORM_NORMAL); - } - - // Apply the scale after sorting out the mode, because the scale - // auto-detection reads the pending output size - if (oc && oc->scale > 0) { - // The factional-scale-v1 protocol uses increments of 120ths to send - // the scale factor to the client. Adjust the scale so that we use the - // same value as the clients'. - wlr_output_state_set_scale(pending, round(oc->scale * 120) / 120); - } else { - wlr_output_state_set_scale(pending, - compute_default_scale(wlr_output, pending)); - } - - if (wlr_output->adaptive_sync_supported) { - if (oc && oc->adaptive_sync != -1) { - wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); - } else { - wlr_output_state_set_adaptive_sync_enabled(pending, false); - } - } - - enum render_bit_depth render_bit_depth = get_config_render_bit_depth(oc); - if (render_bit_depth == RENDER_BIT_DEPTH_10 && - bit_depth_from_format(output->wlr_output->render_format) == render_bit_depth) { - // 10-bit was set successfully before, try to save some tests by reusing the format - wlr_output_state_set_render_format(pending, output->wlr_output->render_format); - } else if (render_bit_depth == RENDER_BIT_DEPTH_10) { - wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); - } else if (render_bit_depth == RENDER_BIT_DEPTH_6) { - wlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565); - } else { - wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); - } - - bool hdr = oc && oc->hdr == 1; - bool color_profile = oc && (oc->color_transform != NULL - || oc->color_profile == COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES); - if (hdr && color_profile) { - sway_log(SWAY_ERROR, "Cannot use HDR on output %s: output has a color profile set", wlr_output->name); - hdr = false; - } - set_hdr(wlr_output, pending, hdr); + return wlr_output_set_mode(output, best); } -struct config_output_state { - struct wlr_color_transform *color_transform; -}; - -static void config_output_state_finish(struct config_output_state *state) { - wlr_color_transform_unref(state->color_transform); +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 struct wlr_color_transform *color_profile_from_device(struct wlr_output *wlr_output, - struct wlr_color_transform *transfer_function) { - struct wlr_color_primaries srgb_primaries; - wlr_color_primaries_from_named(&srgb_primaries, WLR_COLOR_NAMED_PRIMARIES_SRGB); - - const struct wlr_color_primaries *primaries = wlr_output->default_primaries; - if (primaries == NULL) { - sway_log(SWAY_INFO, "output has no reported color information"); - if (transfer_function) { - wlr_color_transform_ref(transfer_function); - } - return transfer_function; - } else if (memcmp(primaries, &srgb_primaries, sizeof(*primaries)) == 0) { - sway_log(SWAY_INFO, "output reports sRGB colors, no correction needed"); - if (transfer_function) { - wlr_color_transform_ref(transfer_function); - } - return transfer_function; - } else { - sway_log(SWAY_INFO, "Creating color profile from reported color primaries: " - "R(%f, %f) G(%f, %f) B(%f, %f) W(%f, %f)", - primaries->red.x, primaries->red.y, primaries->green.x, primaries->green.y, - primaries->blue.x, primaries->blue.y, primaries->white.x, primaries->white.y); - float matrix[9]; - wlr_color_primaries_transform_absolute_colorimetric(&srgb_primaries, primaries, matrix); - struct wlr_color_transform *matrix_transform = wlr_color_transform_init_matrix(matrix); - if (matrix_transform == NULL) { - return NULL; - } - struct wlr_color_transform *resolved_tf = transfer_function ? - wlr_color_transform_ref(transfer_function) : - wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_GAMMA22); - if (resolved_tf == NULL) { - wlr_color_transform_unref(matrix_transform); - return NULL; - } - struct wlr_color_transform *transforms[] = { matrix_transform, resolved_tf }; - size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]); - struct wlr_color_transform *result = wlr_color_transform_init_pipeline(transforms, transforms_len); - wlr_color_transform_unref(matrix_transform); - wlr_color_transform_unref(resolved_tf); - return result; +static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; } -} -static struct wlr_color_transform *get_color_profile(struct wlr_output *output, - struct output_config *oc) { - if (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM) { - if (oc->color_transform) { - wlr_color_transform_ref(oc->color_transform); - } - return oc->color_transform; - } else if (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES) { - return color_profile_from_device(output, oc->color_transform); - } else { - return NULL; + 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) { + // Remove the CLOEXEC flag + int flags = fcntl(sockets[1], F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + exit(EXIT_FAILURE); + } + if (fcntl(sockets[1], F_SETFD, flags & ~FD_CLOEXEC) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + 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 (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; } -static bool finalize_output_config(struct output_config *oc, struct sway_output *output, - const struct wlr_output_state *applied, const struct config_output_state *config_applied) { - if (output == root->fallback_output) { +bool apply_output_config(struct output_config *oc, struct sway_output *output) { + if (output == root->noop_output) { return false; } struct wlr_output *wlr_output = output->wlr_output; + if (oc && !oc->enabled) { - sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); + // Output is configured to be disabled if (output->enabled) { output_disable(output); wlr_output_layout_remove(root->output_layout, wlr_output); } + wlr_output_enable(wlr_output, false); + return true; + } else if (!output->enabled) { + // Output is not enabled. Enable it, output_enable will call us again. + if (!oc || oc->dpms_state != DPMS_OFF) { + wlr_output_enable(wlr_output, true); + } + output_enable(output, oc); return true; } - enum scale_filter_mode scale_filter_old = output->scale_filter; - enum scale_filter_mode scale_filter_new = oc ? oc->scale_filter : SCALE_FILTER_DEFAULT; - switch (scale_filter_new) { - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? - SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; - break; - case SCALE_FILTER_LINEAR: - case SCALE_FILTER_NEAREST: - output->scale_filter = scale_filter_new; - break; + if (oc && oc->dpms_state == DPMS_ON) { + sway_log(SWAY_DEBUG, "Turning on screen"); + wlr_output_enable(wlr_output, true); } - if (scale_filter_old != output->scale_filter) { - sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, - sway_output_scale_filter_to_string(output->scale_filter)); - wlr_damage_ring_add_whole(&output->scene_output->damage_ring); + + bool modeset_success; + if (oc && oc->width > 0 && oc->height > 0) { + sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, + oc->height, oc->refresh_rate); + modeset_success = + set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); + } else if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + modeset_success = wlr_output_set_mode(wlr_output, mode); + } else { + // Output doesn't support modes + modeset_success = true; + } + if (!modeset_success) { + // Failed to modeset, maybe the output is missing a CRTC. Leave the + // output disabled for now and try again when the output gets the mode + // we asked for. + sway_log(SWAY_ERROR, "Failed to modeset output %s", wlr_output->name); + return false; + } + + if (oc && oc->scale > 0) { + sway_log(SWAY_DEBUG, "Set %s scale to %f", oc->name, oc->scale); + wlr_output_set_scale(wlr_output, oc->scale); + } + 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); } // Find position for it - if (oc && oc->x != INT_MAX && oc->y != INT_MAX) { + if (oc && (oc->x != -1 || oc->y != -1)) { sway_log(SWAY_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); wlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y); } else { wlr_output_layout_add_auto(root->output_layout, wlr_output); } - if (!output->enabled) { - output_enable(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); + + 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; + } } - wlr_color_transform_unref(output->color_transform); - if (config_applied->color_transform != NULL) { - wlr_color_transform_ref(config_applied->color_transform); + if (oc && oc->dpms_state == DPMS_OFF) { + sway_log(SWAY_DEBUG, "Turning off screen"); + wlr_output_enable(wlr_output, false); } - output->color_transform = config_applied->color_transform; - - output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; - output->allow_tearing = oc && oc->allow_tearing > 0; - output->hdr = applied->image_description != NULL; return true; } -static void output_update_position(struct sway_output *output) { - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, output->wlr_output, &output_box); - output->lx = output_box.x; - output->ly = output_box.y; - output->width = output_box.width; - output->height = output_box.height; +static void default_output_config(struct output_config *oc, + struct wlr_output *wlr_output) { + oc->enabled = 1; + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + oc->width = mode->width; + oc->height = mode->height; + oc->refresh_rate = mode->refresh; + } + oc->x = oc->y = -1; + oc->scale = 1; + oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; + oc->dpms_state = DPMS_ON; } -// find_output_config_from_list returns a merged output_config containing all -// stored configuration that applies to the specified output. -static struct output_config *find_output_config_from_list( - struct output_config **configs, size_t configs_len, +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 *result = new_output_config(name); - if (result == NULL) { - return 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]; } - char id[128]; - output_get_identifier(id, sizeof(id), sway_output); + struct output_config *oc_id = NULL; + i = list_seq_find(config->output_configs, output_name_cmp, identifier); + if (i >= 0) { + oc_id = config->output_configs->items[i]; + } - // We take a new config and merge on top, in order, the wildcard config, - // output config by name, and output config by identifier to form the final - // config. If there are multiple matches, they are merged in order. - struct output_config *oc = NULL; - const char *names[] = {"*", name, id, NULL}; - for (const char **name = &names[0]; *name; name++) { - for (size_t idx = 0; idx < configs_len; idx++) { - oc = configs[idx]; - if (strcmp(oc->name, *name) == 0) { - merge_output_config(result, oc); - } + struct output_config *result = result = new_output_config("temp"); + if (config->reloading) { + default_output_config(result, sway_output->wlr_output); + } + 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); + + free(result->name); + result->name = temp; + merge_output_config(result, oc_name); + merge_output_config(result, oc_id); + + sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" + " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" + " (dpms %d)", result->name, result->enabled, result->width, + result->height, result->refresh_rate, result->x, result->y, + result->scale, result->transform, result->background, + result->background_option, result->dpms_state); + } else if (oc_name) { + // No identifier config, just return a copy of the name config + free(result->name); + result->name = strdup(name); + merge_output_config(result, oc_name); + } else if (oc_id) { + // No name config, just return a copy of the identifier config + 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("*"); + i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + merge_output_config(result, config->output_configs->items[i]); } + } else { + free_output_config(result); + result = NULL; } return result; } -struct output_config *find_output_config(struct sway_output *sway_output) { - return find_output_config_from_list( - (struct output_config **)config->output_configs->items, - config->output_configs->length, sway_output); -} - -static bool config_has_manual_mode(struct output_config *oc) { - if (!oc) { - return false; - } - if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) { - return true; - } else if (oc->width > 0 && oc->height > 0) { - return true; - } - return false; -} - -/** - * An output config pre-matched to an output - */ -struct matched_output_config { - struct sway_output *output; - struct output_config *config; -}; - -struct search_context { - struct wlr_output_swapchain_manager *swapchain_mgr; - struct wlr_backend_output_state *states; - struct matched_output_config *configs; - size_t configs_len; - bool degrade_to_off; -}; - -static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) { - sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name); - if (state->committed & WLR_OUTPUT_STATE_ENABLED) { - sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no"); - } - if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { - char *format_name = drmGetFormatName(state->render_format); - sway_log(SWAY_DEBUG, " render_format: %s", format_name); - free(format_name); - } - if (state->committed & WLR_OUTPUT_STATE_MODE) { - if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) { - sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz", - state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh); - } else { - sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s", - state->mode->width, state->mode->height, state->mode->refresh, - state->mode->preferred ? " (preferred)" : ""); - } - } - if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { - sway_log(SWAY_DEBUG, " adaptive_sync: %s", - state->adaptive_sync_enabled ? "enabled": "disabled"); - } - if (state->committed & WLR_OUTPUT_STATE_SCALE) { - sway_log(SWAY_DEBUG, " scale: %f", state->scale); - } - if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { - sway_log(SWAY_DEBUG, " subpixel: %s", - sway_wl_output_subpixel_to_string(state->subpixel)); - } -} - -static bool search_valid_config(struct search_context *ctx, size_t output_idx); - -static void reset_output_state(struct wlr_output_state *state) { - wlr_output_state_finish(state); - wlr_output_state_init(state); - state->committed = 0; -} - -static void clear_later_output_states(struct wlr_backend_output_state *states, - size_t configs_len, size_t output_idx) { - - // Clear and disable all output states after this one to avoid conflict - // with previous tests. - for (size_t idx = output_idx+1; idx < configs_len; idx++) { - struct wlr_backend_output_state *backend_state = &states[idx]; - struct wlr_output_state *state = &backend_state->base; - - reset_output_state(state); - wlr_output_state_set_enabled(state, false); - } -} - -static bool search_finish(struct search_context *ctx, size_t output_idx) { - struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; - struct wlr_output_state *state = &backend_state->base; - struct wlr_output *wlr_output = backend_state->output; - - clear_later_output_states(ctx->states, ctx->configs_len, output_idx); - dump_output_state(wlr_output, state); - return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) && - search_valid_config(ctx, output_idx+1); -} - -static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) { - struct matched_output_config *cfg = &ctx->configs[output_idx]; - struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; - struct wlr_output_state *state = &backend_state->base; - - if (!backend_state->output->adaptive_sync_supported) { - return search_finish(ctx, output_idx); - } - - if (cfg->config && cfg->config->adaptive_sync == 1) { - wlr_output_state_set_adaptive_sync_enabled(state, true); - if (search_finish(ctx, output_idx)) { - return true; - } - } - - wlr_output_state_set_adaptive_sync_enabled(state, false); - return search_finish(ctx, output_idx); -} - -static bool search_mode(struct search_context *ctx, size_t output_idx) { - struct matched_output_config *cfg = &ctx->configs[output_idx]; - struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; - struct wlr_output_state *state = &backend_state->base; - struct wlr_output *wlr_output = backend_state->output; - - // We only search for mode if one is not explicitly specified in the config - if (config_has_manual_mode(cfg->config)) { - return search_adaptive_sync(ctx, output_idx); - } - - struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); - if (preferred_mode) { - wlr_output_state_set_mode(state, preferred_mode); - if (search_adaptive_sync(ctx, output_idx)) { - return true; - } - } - - if (wl_list_empty(&wlr_output->modes)) { - state->committed &= ~WLR_OUTPUT_STATE_MODE; - return search_adaptive_sync(ctx, output_idx); - } - - struct wlr_output_mode *mode; - wl_list_for_each(mode, &backend_state->output->modes, link) { - if (mode == preferred_mode) { - continue; - } - wlr_output_state_set_mode(state, mode); - if (search_adaptive_sync(ctx, output_idx)) { - return true; - } - } - - return false; -} - -static bool search_render_format(struct search_context *ctx, size_t output_idx) { - struct matched_output_config *cfg = &ctx->configs[output_idx]; - struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; - struct wlr_output_state *state = &backend_state->base; - struct wlr_output *wlr_output = backend_state->output; - - uint32_t fmts[] = { - DRM_FORMAT_XRGB2101010, - DRM_FORMAT_XBGR2101010, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_RGB565, - DRM_FORMAT_INVALID, - }; - if (render_format_is_bgr(wlr_output->render_format)) { - // Start with BGR in the unlikely event that we previously required it. - fmts[0] = DRM_FORMAT_XBGR2101010; - fmts[1] = DRM_FORMAT_XRGB2101010; - } - - const struct wlr_drm_format_set *primary_formats = - wlr_output_get_primary_formats(wlr_output, server.allocator->buffer_caps); - enum render_bit_depth needed_bits = get_config_render_bit_depth(cfg->config); - for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) { - enum render_bit_depth format_bits = bit_depth_from_format(fmts[idx]); - if (needed_bits < format_bits) { - continue; - } - // If primary_formats is NULL, all formats are supported - if (primary_formats && !wlr_drm_format_set_get(primary_formats, fmts[idx])) { - // This is not a supported format for this output - continue; - } - wlr_output_state_set_render_format(state, fmts[idx]); - if (search_mode(ctx, output_idx)) { - return true; - } - } - return false; -} - -static bool search_valid_config(struct search_context *ctx, size_t output_idx) { - if (output_idx >= ctx->configs_len) { - // We reached the end of the search, all good! - return true; - } - - struct matched_output_config *cfg = &ctx->configs[output_idx]; - struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; - struct wlr_output_state *state = &backend_state->base; - struct wlr_output *wlr_output = backend_state->output; - - if (!output_config_is_disabling(cfg->config)) { - // Search through our possible configurations, doing a depth-first - // through render_format, modes, adaptive_sync and the next output's - // config. - queue_output_config(cfg->config, cfg->output, &backend_state->base); - if (search_render_format(ctx, output_idx)) { - return true; - } else if (!ctx->degrade_to_off) { - return false; - } - // We could not get anything to work, try to disable this output to see - // if we can at least make the outputs before us work. - sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling", - wlr_output->name); - reset_output_state(state); - } - - wlr_output_state_set_enabled(state, false); - return search_finish(ctx, output_idx); -} - -static int compare_matched_output_config_priority(const void *a, const void *b) { - - const struct matched_output_config *amc = a; - const struct matched_output_config *bmc = b; - bool a_disabling = output_config_is_disabling(amc->config); - bool b_disabling = output_config_is_disabling(bmc->config); - bool a_enabled = amc->output->enabled; - bool b_enabled = bmc->output->enabled; - - // We want to give priority to existing enabled outputs. To do so, we want - // the configuration order to be: - // 1. Existing, enabled outputs - // 2. Outputs that need to be enabled - // 3. Disabled or disabling outputs - if (a_enabled && !a_disabling) { - return -1; - } else if (b_enabled && !b_disabling) { - return 1; - } else if (b_disabling && !a_disabling) { - return -1; - } else if (a_disabling && !b_disabling) { - return 1; - } - return 0; -} - -static void sort_output_configs_by_priority( - struct matched_output_config *configs, size_t configs_len) { - qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority); -} - -static bool apply_resolved_output_configs(struct matched_output_config *configs, - size_t configs_len, bool test_only, bool degrade_to_off) { - struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); - if (!states) { - return false; - } - struct config_output_state *config_states = calloc(configs_len, sizeof(*config_states)); - if (!config_states) { - free(states); - return false; - } - - sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - struct wlr_backend_output_state *backend_state = &states[idx]; - struct config_output_state *config_state = &config_states[idx]; - - backend_state->output = cfg->output->wlr_output; - wlr_output_state_init(&backend_state->base); - - queue_output_config(cfg->config, cfg->output, &backend_state->base); - dump_output_state(cfg->output->wlr_output, &backend_state->base); - - config_state->color_transform = get_color_profile(cfg->output->wlr_output, cfg->config); - } - - struct wlr_output_swapchain_manager swapchain_mgr; - wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend); - - bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); - if (!ok) { - sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks"); - struct search_context ctx = { - .swapchain_mgr = &swapchain_mgr, - .states = states, - .configs = configs, - .configs_len = configs_len, - .degrade_to_off = degrade_to_off, - }; - if (!search_valid_config(&ctx, 0)) { - sway_log(SWAY_ERROR, "Search for valid config failed"); - goto out; - } - } - - if (test_only) { - // The swapchain manager already did a test for us - goto out; - } - - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - struct wlr_backend_output_state *backend_state = &states[idx]; - struct config_output_state *config_state = &config_states[idx]; - - struct wlr_scene_output_state_options opts = { - .swapchain = wlr_output_swapchain_manager_get_swapchain( - &swapchain_mgr, backend_state->output), - .color_transform = config_state->color_transform, - }; - struct wlr_scene_output *scene_output = cfg->output->scene_output; - struct wlr_output_state *state = &backend_state->base; - if (!wlr_scene_output_build_state(scene_output, state, &opts)) { - sway_log(SWAY_ERROR, "Building output state for '%s' failed", - backend_state->output->name); - goto out; - } - } - - ok = wlr_backend_commit(server.backend, states, configs_len); - if (!ok) { - sway_log(SWAY_ERROR, "Backend commit failed"); - goto out; - } - - sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len); - - wlr_output_swapchain_manager_apply(&swapchain_mgr); - - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - struct wlr_backend_output_state *backend_state = &states[idx]; - struct config_output_state *config_state = &config_states[idx]; - sway_log(SWAY_DEBUG, "Finalizing config for %s", - cfg->output->wlr_output->name); - finalize_output_config(cfg->config, cfg->output, &backend_state->base, config_state); - } - - // Output layout being applied in finalize_output_config can shift outputs - // around, so we do a second pass to update positions and arrange. - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - output_update_position(cfg->output); - arrange_layers(cfg->output); - } - - arrange_root(); - arrange_locks(); - update_output_manager_config(&server); - transaction_commit_dirty(); - -out: - wlr_output_swapchain_manager_finish(&swapchain_mgr); - for (size_t idx = 0; idx < configs_len; idx++) { - struct wlr_backend_output_state *backend_state = &states[idx]; - wlr_output_state_finish(&backend_state->base); - config_output_state_finish(&config_states[idx]); - } - free(states); - free(config_states); - - // Reconfigure all devices, since input config may have been applied before - // this output came online, and some config items (like map_to_output) are - // dependent on an output being present. - input_manager_configure_all_input_mappings(); - // Reconfigure the cursor images, since the scale may have changed. - input_manager_configure_xcursor(); - - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); - cursor_rebase(seat->cursor); - } - - return ok; -} - -bool apply_output_configs(struct output_config **ocs, size_t ocs_len, - bool test_only, bool degrade_to_off) { - size_t configs_len = wl_list_length(&root->all_outputs); - struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); - if (!configs) { - return false; - } - - int config_idx = 0; +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 + // will be applied during normal "new output" event from wlroots. + bool wildcard = strcmp(oc->name, "*") == 0; + char id[128]; struct sway_output *sway_output; wl_list_for_each(sway_output, &root->all_outputs, link) { - if (sway_output == root->fallback_output) { - configs_len--; - continue; + 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; + } + } + apply_output_config(current, sway_output); + free_output_config(current); + + if (!wildcard) { + // Stop looking if the output config isn't applicable to all + // outputs + break; + } } - - struct matched_output_config *config = &configs[config_idx++]; - config->output = sway_output; - config->config = find_output_config_from_list(ocs, ocs_len, sway_output); } - - sort_output_configs_by_priority(configs, configs_len); - bool ok = apply_resolved_output_configs(configs, configs_len, test_only, degrade_to_off); - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - free_output_config(cfg->config); - } - free(configs); - return ok; } -void apply_stored_output_configs(void) { - apply_output_configs((struct output_config **)config->output_configs->items, - config->output_configs->length, false, true); +void reset_outputs(void) { + struct output_config *oc = NULL; + int i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + oc = config->output_configs->items[i]; + } else { + oc = store_output_config(new_output_config("*")); + } + apply_output_config_to_outputs(oc); } void free_output_config(struct output_config *oc) { @@ -1164,129 +462,5 @@ void free_output_config(struct output_config *oc) { free(oc->name); free(oc->background); free(oc->background_option); - free(oc->background_fallback); - wlr_color_transform_unref(oc->color_transform); 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 (!sway_set_cloexec(sockets[0], true) || !sway_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) { - if (!sway_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, "failed to execute '%s' " - "(background configuration probably not applied)", - command[0]); - _exit(EXIT_FAILURE); - } - - if (close(sockets[1]) != 0) { - sway_log_errno(SWAY_ERROR, "close 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(length, sizeof(char *)); - 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/config/seat.c b/sway/config/seat.c index f2326189e..04a44e3a8 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -1,4 +1,4 @@ -#include +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/config.h" @@ -17,8 +17,6 @@ struct seat_config *new_seat_config(const char* name) { return NULL; } - seat->idle_inhibit_sources = seat->idle_wake_sources = UINT32_MAX; - seat->fallback = -1; seat->attachments = create_list(); if (!sway_assert(seat->attachments, @@ -28,12 +26,7 @@ struct seat_config *new_seat_config(const char* name) { return NULL; } seat->hide_cursor_timeout = -1; - seat->hide_cursor_when_typing = HIDE_WHEN_TYPING_DEFAULT; seat->allow_constrain = CONSTRAIN_DEFAULT; - seat->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; - seat->keyboard_grouping = KEYBOARD_GROUP_DEFAULT; - seat->xcursor_theme.name = NULL; - seat->xcursor_theme.size = 24; return seat; } @@ -98,6 +91,7 @@ static void seat_attachment_config_free( struct seat_attachment_config *attachment) { free(attachment->identifier); free(attachment); + return; } static struct seat_attachment_config *seat_attachment_config_copy( @@ -150,35 +144,9 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) { dest->hide_cursor_timeout = source->hide_cursor_timeout; } - if (source->hide_cursor_when_typing != HIDE_WHEN_TYPING_DEFAULT) { - dest->hide_cursor_when_typing = source->hide_cursor_when_typing; - } - if (source->allow_constrain != CONSTRAIN_DEFAULT) { dest->allow_constrain = source->allow_constrain; } - - if (source->shortcuts_inhibit != SHORTCUTS_INHIBIT_DEFAULT) { - dest->shortcuts_inhibit = source->shortcuts_inhibit; - } - - if (source->keyboard_grouping != KEYBOARD_GROUP_DEFAULT) { - dest->keyboard_grouping = source->keyboard_grouping; - } - - if (source->xcursor_theme.name != NULL) { - free(dest->xcursor_theme.name); - dest->xcursor_theme.name = strdup(source->xcursor_theme.name); - dest->xcursor_theme.size = source->xcursor_theme.size; - } - - if (source->idle_inhibit_sources != UINT32_MAX) { - dest->idle_inhibit_sources = source->idle_inhibit_sources; - } - - if (source->idle_wake_sources != UINT32_MAX) { - dest->idle_wake_sources = source->idle_wake_sources; - } } struct seat_config *copy_seat_config(struct seat_config *seat) { @@ -202,7 +170,6 @@ void free_seat_config(struct seat_config *seat) { seat_attachment_config_free(seat->attachments->items[i]); } list_free(seat->attachments); - free(seat->xcursor_theme.name); free(seat); } diff --git a/sway/criteria.c b/sway/criteria.c index 6be6e7042..48ef39df4 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -1,13 +1,12 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include #include -#define PCRE2_CODE_UNIT_WIDTH 8 -#include +#include #include "sway/criteria.h" #include "sway/tree/container.h" #include "sway/config.h" -#include "sway/server.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -19,11 +18,10 @@ bool criteria_is_empty(struct criteria *criteria) { return !criteria->title && !criteria->shell - && !criteria->all && !criteria->app_id && !criteria->con_mark && !criteria->con_id -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND && !criteria->class && !criteria->id && !criteria->instance @@ -33,93 +31,30 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->floating && !criteria->tiling && !criteria->urgent - && !criteria->workspace - && !criteria->pid - && !criteria->sandbox_engine - && !criteria->sandbox_app_id - && !criteria->sandbox_instance_id - && !criteria->tag; -} - -// The error pointer is used for parsing functions, and saves having to pass it -// as an argument in several places. -char *error = NULL; - -// Returns error string on failure or NULL otherwise. -static bool generate_regex(pcre2_code **regex, char *value) { - int errorcode; - PCRE2_SIZE offset; - - *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL); - if (!*regex) { - PCRE2_UCHAR buffer[256]; - pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); - - const char *fmt = "Regex compilation for '%s' failed: %s"; - int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3; - error = malloc(len); - snprintf(error, len, fmt, value, buffer); - return false; - } - - return true; -} - -static bool pattern_create(struct pattern **pattern, char *value) { - *pattern = calloc(1, sizeof(struct pattern)); - if (!*pattern) { - sway_log(SWAY_ERROR, "Failed to allocate pattern"); - } - - if (strcmp(value, "__focused__") == 0) { - (*pattern)->match_type = PATTERN_FOCUSED; - } else { - (*pattern)->match_type = PATTERN_PCRE2; - if (!generate_regex(&(*pattern)->regex, value)) { - return false; - }; - } - return true; -} - -static void pattern_destroy(struct pattern *pattern) { - if (pattern) { - if (pattern->regex) { - pcre2_code_free(pattern->regex); - } - free(pattern); - } + && !criteria->workspace; } void criteria_destroy(struct criteria *criteria) { - pattern_destroy(criteria->title); - pattern_destroy(criteria->shell); - pattern_destroy(criteria->app_id); -#if WLR_HAS_XWAYLAND - pattern_destroy(criteria->class); - pattern_destroy(criteria->instance); - pattern_destroy(criteria->window_role); + pcre_free(criteria->title); + pcre_free(criteria->shell); + pcre_free(criteria->app_id); +#if HAVE_XWAYLAND + pcre_free(criteria->class); + pcre_free(criteria->instance); + pcre_free(criteria->window_role); #endif - pattern_destroy(criteria->con_mark); - pattern_destroy(criteria->workspace); - pattern_destroy(criteria->sandbox_engine); - pattern_destroy(criteria->sandbox_app_id); - pattern_destroy(criteria->sandbox_instance_id); - pattern_destroy(criteria->tag); - free(criteria->target); + pcre_free(criteria->con_mark); + free(criteria->workspace); free(criteria->cmdlist); free(criteria->raw); free(criteria); } -static int regex_cmp(const char *item, const pcre2_code *regex) { - pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL); - int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL); - pcre2_match_data_free(match_data); - return result; +static int regex_cmp(const char *item, const pcre *regex) { + return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); } -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND static bool view_has_window_type(struct sway_view *view, enum atom_name name) { if (view->type != SWAY_VIEW_XWAYLAND) { return false; @@ -161,17 +96,34 @@ static void find_urgent_iterator(struct sway_container *con, void *data) { list_add(urgent_views, con->view); } -static bool has_container_criteria(struct criteria *criteria) { - return criteria->con_mark || criteria->con_id; -} +static bool criteria_matches_view(struct criteria *criteria, + struct sway_view *view) { + if (criteria->title) { + const char *title = view_get_title(view); + if (!title || regex_cmp(title, criteria->title) != 0) { + return false; + } + } + + if (criteria->shell) { + const char *shell = view_get_shell(view); + if (!shell || regex_cmp(shell, criteria->shell) != 0) { + return false; + } + } + + if (criteria->app_id) { + const char *app_id = view_get_app_id(view); + if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) { + return false; + } + } -static bool criteria_matches_container(struct criteria *criteria, - struct sway_container *container) { if (criteria->con_mark) { bool exists = false; - struct sway_container *con = container; + struct sway_container *con = view->container; for (int i = 0; i < con->marks->length; ++i) { - if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) { + if (regex_cmp(con->marks->items[i], criteria->con_mark) == 0) { exists = true; break; } @@ -182,169 +134,12 @@ static bool criteria_matches_container(struct criteria *criteria, } if (criteria->con_id) { // Internal ID - if (container->node.id != criteria->con_id) { + if (!view->container || view->container->node.id != criteria->con_id) { return false; } } - return true; -} - -static bool criteria_matches_view(struct criteria *criteria, - struct sway_view *view) { - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - struct sway_view *focused = focus ? focus->view : NULL; - - if (!view->container) { - return false; - } - - if (criteria->title) { - const char *title = view_get_title(view); - if (!title) { - title = ""; - } - - switch (criteria->title->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(title, view_get_title(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(title, criteria->title->regex) < 0) { - return false; - } - break; - } - } - - if (criteria->shell) { - const char *shell = view_get_shell(view); - if (!shell) { - shell = ""; - } - - switch (criteria->shell->match_type) { - case PATTERN_FOCUSED: - if (!focused || strcmp(shell, view_get_shell(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(shell, criteria->shell->regex) < 0) { - return false; - } - break; - } - } - - if (criteria->app_id) { - const char *app_id = view_get_app_id(view); - if (!app_id) { - app_id = ""; - } - - switch (criteria->app_id->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(app_id, view_get_app_id(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(app_id, criteria->app_id->regex) < 0) { - return false; - } - break; - } - } - - if (criteria->sandbox_engine) { - const char *sandbox_engine = view_get_sandbox_engine(view); - if (!sandbox_engine) { - return false; - } - - switch (criteria->sandbox_engine->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(sandbox_engine, view_get_sandbox_engine(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(sandbox_engine, criteria->sandbox_engine->regex) < 0) { - return false; - } - break; - } - } - - if (criteria->sandbox_app_id) { - const char *sandbox_app_id = view_get_sandbox_app_id(view); - if (!sandbox_app_id) { - return false; - } - - switch (criteria->sandbox_app_id->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(sandbox_app_id, view_get_sandbox_app_id(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(sandbox_app_id, criteria->sandbox_app_id->regex) < 0) { - return false; - } - break; - } - } - - if (criteria->sandbox_instance_id) { - const char *sandbox_instance_id = view_get_sandbox_instance_id(view); - if (!sandbox_instance_id) { - return false; - } - - switch (criteria->sandbox_instance_id->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(sandbox_instance_id, view_get_sandbox_instance_id(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(sandbox_instance_id, criteria->sandbox_instance_id->regex) < 0) { - return false; - } - break; - } - } - - if (criteria->tag) { - const char *tag = view_get_tag(view); - if (!tag) { - return false; - } - - switch (criteria->tag->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(tag, view_get_tag(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(tag, criteria->tag->regex) < 0) { - return false; - } - break; - } - } - - if (!criteria_matches_container(criteria, view->container)) { - return false; - } - -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND if (criteria->id) { // X11 window ID uint32_t x11_window_id = view_get_x11_window_id(view); if (!x11_window_id || x11_window_id != criteria->id) { @@ -354,61 +149,22 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->class) { const char *class = view_get_class(view); - if (!class) { - class = ""; - } - - switch (criteria->class->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(class, view_get_class(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(class, criteria->class->regex) < 0) { - return false; - } - break; + if (!class || regex_cmp(class, criteria->class) != 0) { + return false; } } if (criteria->instance) { const char *instance = view_get_instance(view); - if (!instance) { - instance = ""; - } - - switch (criteria->instance->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(instance, view_get_instance(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(instance, criteria->instance->regex) < 0) { - return false; - } - break; + if (!instance || regex_cmp(instance, criteria->instance) != 0) { + return false; } } if (criteria->window_role) { - const char *window_role = view_get_window_role(view); - if (!window_role) { - window_role = ""; - } - - switch (criteria->window_role->match_type) { - case PATTERN_FOCUSED: - if (!focused || lenient_strcmp(window_role, view_get_window_role(focused))) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(window_role, criteria->window_role->regex) < 0) { - return false; - } - break; + const char *role = view_get_window_role(view); + if (!role || regex_cmp(role, criteria->window_role) != 0) { + return false; } } @@ -451,28 +207,8 @@ static bool criteria_matches_view(struct criteria *criteria, } if (criteria->workspace) { - struct sway_workspace *ws = view->container->pending.workspace; - if (!ws) { - return false; - } - - switch (criteria->workspace->match_type) { - case PATTERN_FOCUSED: - if (!focused || - strcmp(ws->name, focused->container->pending.workspace->name)) { - return false; - } - break; - case PATTERN_PCRE2: - if (regex_cmp(ws->name, criteria->workspace->regex) < 0) { - return false; - } - break; - } - } - - if (criteria->pid) { - if (criteria->pid != view->pid) { + struct sway_workspace *ws = view->container->workspace; + if (!ws || strcmp(ws->name, criteria->workspace) != 0) { return false; } } @@ -497,31 +233,49 @@ struct match_data { list_t *matches; }; -static void criteria_get_containers_iterator(struct sway_container *container, +static void criteria_get_views_iterator(struct sway_container *container, void *data) { struct match_data *match_data = data; if (container->view) { if (criteria_matches_view(match_data->criteria, container->view)) { - list_add(match_data->matches, container); - } - } else if (has_container_criteria(match_data->criteria)) { - if (criteria_matches_container(match_data->criteria, container)) { - list_add(match_data->matches, container); + list_add(match_data->matches, container->view); } } } -list_t *criteria_get_containers(struct criteria *criteria) { +list_t *criteria_get_views(struct criteria *criteria) { list_t *matches = create_list(); struct match_data data = { .criteria = criteria, .matches = matches, }; - root_for_each_container(criteria_get_containers_iterator, &data); + root_for_each_container(criteria_get_views_iterator, &data); return matches; } -#if WLR_HAS_XWAYLAND +// The error pointer is used for parsing functions, and saves having to pass it +// as an argument in several places. +char *error = NULL; + +// Returns error string on failure or NULL otherwise. +static bool generate_regex(pcre **regex, char *value) { + const char *reg_err; + int offset; + + *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); + + if (!*regex) { + const char *fmt = "Regex compilation for '%s' failed: %s"; + int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; + error = malloc(len); + snprintf(error, len, fmt, value, reg_err); + return false; + } + + return true; +} + +#if HAVE_XWAYLAND static enum atom_name parse_window_type(const char *type) { if (strcasecmp(type, "normal") == 0) { return NET_WM_WINDOW_TYPE_NORMAL; @@ -549,12 +303,11 @@ static enum atom_name parse_window_type(const char *type) { #endif enum criteria_token { - T_ALL, T_APP_ID, T_CON_ID, T_CON_MARK, T_FLOATING, -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND T_CLASS, T_ID, T_INSTANCE, @@ -566,25 +319,18 @@ enum criteria_token { T_TITLE, T_URGENT, T_WORKSPACE, - T_PID, - T_SANDBOX_ENGINE, - T_SANDBOX_APP_ID, - T_SANDBOX_INSTANCE_ID, - T_TAG, T_INVALID, }; static enum criteria_token token_from_name(char *name) { - if (strcmp(name, "all") == 0) { - return T_ALL; - } else if (strcmp(name, "app_id") == 0) { + if (strcmp(name, "app_id") == 0) { return T_APP_ID; } else if (strcmp(name, "con_id") == 0) { return T_CON_ID; } else if (strcmp(name, "con_mark") == 0) { return T_CON_MARK; -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND } else if (strcmp(name, "class") == 0) { return T_CLASS; } else if (strcmp(name, "id") == 0) { @@ -608,20 +354,79 @@ static enum criteria_token token_from_name(char *name) { return T_TILING; } else if (strcmp(name, "floating") == 0) { return T_FLOATING; - } else if (strcmp(name, "pid") == 0) { - return T_PID; - } else if (strcmp(name, "sandbox_engine") == 0) { - return T_SANDBOX_ENGINE; - } else if (strcmp(name, "sandbox_app_id") == 0) { - return T_SANDBOX_APP_ID; - } else if (strcmp(name, "sandbox_instance_id") == 0) { - return T_SANDBOX_INSTANCE_ID; - } else if (strcmp(name, "tag") == 0) { - return T_TAG; } return T_INVALID; } +/** + * Get a property of the focused view. + * + * Note that we are taking the focused view at the time of criteria parsing, not + * at the time of execution. This is because __focused__ only makes sense when + * 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) { + 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; + const char *value = NULL; + + switch (token) { + case T_APP_ID: + value = view_get_app_id(view); + break; + case T_SHELL: + value = view_get_shell(view); + break; + case T_TITLE: + value = view_get_title(view); + break; + case T_WORKSPACE: + if (focus->workspace) { + value = focus->workspace->name; + } + break; + case T_CON_ID: + if (view->container == NULL) { + return NULL; + } + 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); + break; + case T_INSTANCE: + value = view_get_instance(view); + break; + case T_WINDOW_ROLE: + value = view_get_window_role(view); + break; + case T_WINDOW_TYPE: // These do not support __focused__ + case T_ID: +#endif + case T_CON_MARK: + case T_FLOATING: + case T_TILING: + case T_URGENT: + case T_INVALID: + break; + } + if (value) { + return strdup(value); + } + return NULL; +} + static bool parse_token(struct criteria *criteria, char *name, char *value) { enum criteria_token token = token_from_name(name); if (token == T_INVALID) { @@ -632,8 +437,15 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { return false; } - // Require value, unless token is all, floating or tiled - if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) { + char *effective_value = NULL; + if (value && strcmp(value, "__focused__") == 0) { + effective_value = get_focused_prop(token); + } else if (value) { + effective_value = strdup(value); + } + + // Require value, unless token is floating or tiled + if (!effective_value && token != T_FLOATING && token != T_TILING) { const char *fmt = "Token '%s' requires a value"; int len = strlen(fmt) + strlen(name) - 1; error = malloc(len); @@ -643,51 +455,42 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { char *endptr = NULL; switch (token) { - case T_ALL: - criteria->all = true; - break; case T_TITLE: - pattern_create(&criteria->title, value); + generate_regex(&criteria->title, effective_value); break; case T_SHELL: - pattern_create(&criteria->shell, value); + generate_regex(&criteria->shell, effective_value); break; case T_APP_ID: - pattern_create(&criteria->app_id, value); + generate_regex(&criteria->app_id, effective_value); break; case T_CON_ID: - if (strcmp(value, "__focused__") == 0) { - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - criteria->con_id = focus ? focus->node.id : 0; - } else { - criteria->con_id = strtoul(value, &endptr, 10); - if (*endptr != 0) { - error = strdup("The value for 'con_id' should be '__focused__' or numeric"); - } + criteria->con_id = strtoul(effective_value, &endptr, 10); + if (*endptr != 0) { + error = strdup("The value for 'con_id' should be '__focused__' or numeric"); } break; case T_CON_MARK: - pattern_create(&criteria->con_mark, value); + generate_regex(&criteria->con_mark, effective_value); break; -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND case T_CLASS: - pattern_create(&criteria->class, value); + generate_regex(&criteria->class, effective_value); break; case T_ID: - criteria->id = strtoul(value, &endptr, 10); + criteria->id = strtoul(effective_value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'id' should be numeric"); } break; case T_INSTANCE: - pattern_create(&criteria->instance, value); + generate_regex(&criteria->instance, effective_value); break; case T_WINDOW_ROLE: - pattern_create(&criteria->window_role, value); + generate_regex(&criteria->window_role, effective_value); break; case T_WINDOW_TYPE: - criteria->window_type = parse_window_type(value); + criteria->window_type = parse_window_type(effective_value); break; #endif case T_FLOATING: @@ -697,13 +500,13 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { criteria->tiling = true; break; case T_URGENT: - if (strcmp(value, "latest") == 0 || - strcmp(value, "newest") == 0 || - strcmp(value, "last") == 0 || - strcmp(value, "recent") == 0) { + if (strcmp(effective_value, "latest") == 0 || + strcmp(effective_value, "newest") == 0 || + strcmp(effective_value, "last") == 0 || + strcmp(effective_value, "recent") == 0) { criteria->urgent = 'l'; - } else if (strcmp(value, "oldest") == 0 || - strcmp(value, "first") == 0) { + } else if (strcmp(effective_value, "oldest") == 0 || + strcmp(effective_value, "first") == 0) { criteria->urgent = 'o'; } else { error = @@ -712,29 +515,12 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { } break; case T_WORKSPACE: - pattern_create(&criteria->workspace, value); - break; - case T_PID: - criteria->pid = strtoul(value, &endptr, 10); - if (*endptr != 0) { - error = strdup("The value for 'pid' should be numeric"); - } - break; - case T_SANDBOX_ENGINE: - pattern_create(&criteria->sandbox_engine, value); - break; - case T_SANDBOX_APP_ID: - pattern_create(&criteria->sandbox_app_id, value); - break; - case T_SANDBOX_INSTANCE_ID: - pattern_create(&criteria->sandbox_instance_id, value); - break; - case T_TAG: - pattern_create(&criteria->tag, value); + criteria->workspace = strdup(effective_value); break; case T_INVALID: break; } + free(effective_value); if (error) { return false; @@ -790,7 +576,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { ++head; struct criteria *criteria = calloc(1, sizeof(struct criteria)); -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND criteria->window_type = ATOM_LAST; // default value #endif char *name = NULL, *value = NULL; @@ -805,7 +591,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } name = calloc(head - namestart + 1, 1); if (head != namestart) { - memcpy(name, namestart, head - namestart); + strncpy(name, namestart, head - namestart); } // Parse token value skip_spaces(&head); @@ -832,14 +618,14 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } } value = calloc(head - valuestart + 1, 1); - memcpy(value, valuestart, head - valuestart); + strncpy(value, valuestart, head - valuestart); if (in_quotes) { ++head; 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; @@ -863,7 +649,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { ++head; int len = head - raw; criteria->raw = calloc(len + 1, 1); - memcpy(criteria->raw, raw, len); + strncpy(criteria->raw, raw, len); return criteria; cleanup: @@ -872,36 +658,3 @@ cleanup: criteria_destroy(criteria); return NULL; } - -bool criteria_is_equal(struct criteria *left, struct criteria *right) { - if (left->type != right->type) { - return false; - } - // XXX Only implemented for CT_NO_FOCUS for now. - if (left->type == CT_NO_FOCUS) { - return strcmp(left->raw, right->raw) == 0; - } - if (left->type == CT_COMMAND) { - return strcmp(left->raw, right->raw) == 0 - && strcmp(left->cmdlist, right->cmdlist) == 0; - } - return false; -} - -bool criteria_already_exists(struct criteria *criteria) { - // XXX Only implemented for CT_NO_FOCUS and CT_COMMAND for now. - // While criteria_is_equal also obeys this limitation, this is a shortcut - // to avoid processing the list. - if (criteria->type != CT_NO_FOCUS && criteria->type != CT_COMMAND) { - return false; - } - - list_t *criterias = config->criteria; - for (int i = 0; i < criterias->length; ++i) { - struct criteria *existing = criterias->items[i]; - if (criteria_is_equal(criteria, existing)) { - return true; - } - } - return false; -} diff --git a/sway/debug-tree.c b/sway/debug-tree.c new file mode 100644 index 000000000..0444bb3fd --- /dev/null +++ b/sway/debug-tree.c @@ -0,0 +1,161 @@ +#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 new file mode 100644 index 000000000..d8dd02403 --- /dev/null +++ b/sway/desktop/desktop.c @@ -0,0 +1,37 @@ +#include "sway/tree/container.h" +#include "sway/desktop.h" +#include "sway/output.h" + +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); + } +} + +void desktop_damage_whole_container(struct sway_container *con) { + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole_container(output, con); + } +} + +void desktop_damage_box(struct wlr_box *box) { + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_box(output, box); + } +} + +void desktop_damage_view(struct sway_view *view) { + desktop_damage_whole_container(view->container); + struct wlr_box box = { + .x = view->container->current.content_x - view->geometry.x, + .y = view->container->current.content_y - view->geometry.y, + .width = view->surface->current.width, + .height = view->surface->current.height, + }; + desktop_damage_box(&box); +} diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 6b2761fcd..87b4ef43d 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,26 +1,19 @@ #include -#include -#include +#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(); - 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"); - destroy_inhibitor(inhibitor); + wl_list_remove(&inhibitor->link); + wl_list_remove(&inhibitor->destroy.link); + idle_inhibit_v1_check_active(inhibitor->manager); + free(inhibitor); } void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { @@ -35,160 +28,53 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { return; } - inhibitor->mode = INHIBIT_IDLE_APPLICATION; - inhibitor->wlr_inhibitor = wlr_inhibitor; + inhibitor->manager = manager; + 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); - sway_idle_inhibit_v1_check_active(); + idle_inhibit_v1_check_active(manager); } -void handle_manager_destroy(struct wl_listener *listener, void *data) { - struct sway_idle_inhibit_manager_v1 *manager = - wl_container_of(listener, manager, manager_destroy); - - wl_list_remove(&manager->manager_destroy.link); - wl_list_remove(&manager->new_idle_inhibitor_v1.link); -} - -void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, - enum sway_idle_inhibit_mode mode) { - struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; - - struct sway_idle_inhibitor_v1 *inhibitor = - calloc(1, sizeof(struct sway_idle_inhibitor_v1)); - if (!inhibitor) { - return; - } - - inhibitor->mode = mode; - inhibitor->view = view; - wl_list_insert(&manager->inhibitors, &inhibitor->link); - - inhibitor->destroy.notify = handle_destroy; - wl_signal_add(&view->events.unmap, &inhibitor->destroy); - - sway_idle_inhibit_v1_check_active(); -} - -struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( - struct sway_view *view) { - struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; - struct sway_idle_inhibitor_v1 *inhibitor; - wl_list_for_each(inhibitor, &manager->inhibitors, link) { - if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && - inhibitor->view == view) { - return inhibitor; - } - } - return NULL; -} - -struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( - struct sway_view *view) { - struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; - struct sway_idle_inhibitor_v1 *inhibitor; - wl_list_for_each(inhibitor, &manager->inhibitors, link) { - if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && - view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { - 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); -} - -bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { - if (server.session_lock.lock) { - // A session lock is active. In this case, only application inhibitors - // on the session lock surface can have any effect. - if (inhibitor->mode != INHIBIT_IDLE_APPLICATION) { - return false; - } - struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; - if (!wlr_session_lock_surface_v1_try_from_wlr_surface(wlr_surface)) { - return false; - } - return wlr_surface->mapped; - } - - switch (inhibitor->mode) { - case INHIBIT_IDLE_APPLICATION:; - struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); - if (layer_surface) { - // Layer surfaces can be occluded but are always on screen after - // they have been mapped. - return layer_surface->output && layer_surface->output->enabled && - wlr_surface->mapped; - } - - // If there is no view associated with the inhibitor, assume invisible - struct sway_view *view = view_from_wlr_surface(wlr_surface); - return view && view->container && view_is_visible(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(void) { - struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; +void 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 ((inhibited = sway_idle_inhibit_v1_is_active(inhibitor))) { + 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; break; } } - wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); + wlr_idle_set_enabled(manager->idle, NULL, !inhibited); } -bool sway_idle_inhibit_manager_v1_init(void) { - struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; - - manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display); - if (!manager->wlr_manager) { - return false; +struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( + struct wl_display *wl_display, struct wlr_idle *idle) { + struct sway_idle_inhibit_manager_v1 *manager = + calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); + if (!manager) { + return NULL; } + manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); + if (!manager->wlr_manager) { + free(manager); + return NULL; + } + manager->idle = idle; wl_signal_add(&manager->wlr_manager->events.new_inhibitor, &manager->new_idle_inhibitor_v1); manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; - wl_signal_add(&manager->wlr_manager->events.destroy, - &manager->manager_destroy); - manager->manager_destroy.notify = handle_manager_destroy; wl_list_init(&manager->inhibitors); - return true; + return manager; } diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c deleted file mode 100644 index 2362e1ba0..000000000 --- a/sway/desktop/launcher.c +++ /dev/null @@ -1,268 +0,0 @@ -#include -#include -#include -#include "sway/input/seat.h" -#include "sway/output.h" -#include "sway/desktop/launcher.h" -#include "sway/server.h" -#include "sway/tree/node.h" -#include "sway/tree/container.h" -#include "sway/tree/workspace.h" -#include "sway/tree/root.h" -#include "log.h" - -/** - * Get the pid of a parent process given the pid of a child process. - * - * Returns the parent pid or NULL if the parent pid cannot be determined. - */ -static pid_t get_parent_pid(pid_t child) { - pid_t parent = -1; - char file_name[100]; - char *buffer = NULL; - const char *sep = " "; - FILE *stat = NULL; - size_t buf_size = 0; - - snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); - - if ((stat = fopen(file_name, "r"))) { - if (getline(&buffer, &buf_size, stat) != -1) { - strtok(buffer, sep); // pid - strtok(NULL, sep); // executable name - strtok(NULL, sep); // state - char *token = strtok(NULL, sep); // parent pid - parent = strtol(token, NULL, 10); - } - free(buffer); - fclose(stat); - } - - if (parent) { - return (parent == child) ? -1 : parent; - } - - return -1; -} - -void launcher_ctx_consume(struct launcher_ctx *ctx) { - // The view is now responsible for destroying this ctx - wl_list_remove(&ctx->token_destroy.link); - wl_list_init(&ctx->token_destroy.link); - - if (!ctx->activated) { - // An unactivated token hasn't been destroyed yet - wlr_xdg_activation_token_v1_destroy(ctx->token); - } - ctx->token = NULL; - - // Prevent additional matches - wl_list_remove(&ctx->link); - wl_list_init(&ctx->link); -} - -void launcher_ctx_destroy(struct launcher_ctx *ctx) { - if (ctx == NULL) { - return; - } - wl_list_remove(&ctx->node_destroy.link); - wl_list_remove(&ctx->token_destroy.link); - if (ctx->seat) { - wl_list_remove(&ctx->seat_destroy.link); - } - wl_list_remove(&ctx->link); - wlr_xdg_activation_token_v1_destroy(ctx->token); - free(ctx->fallback_name); - free(ctx); -} - -struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) { - if (wl_list_empty(&server.pending_launcher_ctxs)) { - return NULL; - } - - struct launcher_ctx *ctx = NULL; - sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); - - do { - struct launcher_ctx *_ctx = NULL; - wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) { - if (pid == _ctx->pid) { - ctx = _ctx; - sway_log(SWAY_DEBUG, - "found %s match for pid %d: %s", - node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node)); - break; - } - } - pid = get_parent_pid(pid); - } while (pid > 1); - - return ctx; -} - -struct sway_workspace *launcher_ctx_get_workspace( - struct launcher_ctx *ctx) { - struct sway_workspace *ws = NULL; - struct sway_output *output = NULL; - - switch (ctx->node->type) { - case N_CONTAINER: - // Unimplemented - // TODO: add container matching? - ws = ctx->node->sway_container->pending.workspace; - break; - case N_WORKSPACE: - ws = ctx->node->sway_workspace; - break; - case N_OUTPUT: - output = ctx->node->sway_output; - ws = workspace_by_name(ctx->fallback_name); - if (!ws) { - sway_log(SWAY_DEBUG, - "Creating workspace %s for pid %d because it disappeared", - ctx->fallback_name, ctx->pid); - if (!output->enabled) { - sway_log(SWAY_DEBUG, - "Workspace output %s is disabled, trying another one", - output->wlr_output->name); - output = NULL; - } - ws = workspace_create(output, ctx->fallback_name); - } - break; - case N_ROOT: - ws = workspace_create(NULL, ctx->fallback_name); - break; - } - - return ws; -} - -static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { - struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy); - switch (ctx->node->type) { - case N_CONTAINER: - // Unimplemented - break; - case N_WORKSPACE:; - struct sway_workspace *ws = ctx->node->sway_workspace; - wl_list_remove(&ctx->node_destroy.link); - wl_list_init(&ctx->node_destroy.link); - // We want to save this ws name to recreate later, hopefully on the - // same output - free(ctx->fallback_name); - ctx->fallback_name = strdup(ws->name); - if (!ws->output || ws->output->node.destroying) { - // If the output is being destroyed it would be pointless to track - // If the output is being disabled, we'll find out if it's still - // disabled when we try to match it. - ctx->node = &root->node; - break; - } - ctx->node = &ws->output->node; - wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); - break; - case N_OUTPUT: - wl_list_remove(&ctx->node_destroy.link); - wl_list_init(&ctx->node_destroy.link); - // We'll make the ws ctx->name somewhere else - ctx->node = &root->node; - break; - case N_ROOT: - // Unreachable - break; - } -} - -static void token_handle_destroy(struct wl_listener *listener, void *data) { - struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy); - ctx->token = NULL; - launcher_ctx_destroy(ctx); -} - -struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token, - struct sway_node *node) { - struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); - - const char *fallback_name = NULL; - struct sway_workspace *ws = NULL; - switch (node->type) { - case N_CONTAINER: - // Unimplemented - free(ctx); - return NULL; - case N_WORKSPACE: - ws = node->sway_workspace; - fallback_name = ws->name; - break; - case N_OUTPUT:; - struct sway_output *output = node->sway_output; - ws = output_get_active_workspace(output); - fallback_name = ws ? ws->name : NULL; - break; - case N_ROOT: - // Unimplemented - free(ctx); - return NULL; - } - - if (!fallback_name) { - // TODO: implement a better fallback. - free(ctx); - return NULL; - } - - ctx->fallback_name = strdup(fallback_name); - ctx->token = token; - ctx->node = node; - // Having surface set means that the focus check in wlroots has passed - ctx->had_focused_surface = token->surface != NULL; - - ctx->node_destroy.notify = ctx_handle_node_destroy; - wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); - - ctx->token_destroy.notify = token_handle_destroy; - wl_signal_add(&token->events.destroy, &ctx->token_destroy); - - wl_list_init(&ctx->link); - wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); - - token->data = ctx; - return ctx; -} - -static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) { - struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy); - ctx->seat = NULL; - wl_list_remove(&ctx->seat_destroy.link); -} - -// Creates a context with a new token for the internal launcher -struct launcher_ctx *launcher_ctx_create_internal(void) { - struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (!ws) { - sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); - return NULL; - } - - struct wlr_xdg_activation_token_v1 *token = - wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); - - struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); - if (!ctx) { - wlr_xdg_activation_token_v1_destroy(token); - return NULL; - } - ctx->seat = seat; - ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy; - wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy); - - return ctx; -} - -const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) { - const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token); - return token; -} diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index c8f485971..0767247c4 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -1,15 +1,12 @@ #include #include #include -#include -#include +#include +#include #include +#include #include -#include -#include -#include #include "log.h" -#include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -20,59 +17,144 @@ #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" -struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( - struct wlr_surface *surface) { - struct wlr_layer_surface_v1 *layer; - do { - if (!surface) { - return NULL; - } - // Topmost layer surface - if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) { - return layer; - } - // Layer subsurface - if (wlr_subsurface_try_from_wlr_surface(surface)) { - surface = wlr_surface_get_root_surface(surface); - continue; - } - - // Layer surface popup - struct wlr_xdg_surface *xdg_surface = NULL; - if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) && - xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { - if (!xdg_surface->popup->parent) { - return NULL; +static void apply_exclusive(struct wlr_box *usable_area, + uint32_t anchor, int32_t exclusive, + int32_t margin_top, int32_t margin_right, + int32_t margin_bottom, int32_t margin_left) { + if (exclusive <= 0) { + return; + } + struct { + uint32_t anchors; + int *positive_axis; + int *negative_axis; + int margin; + } edges[] = { + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .positive_axis = &usable_area->y, + .negative_axis = &usable_area->height, + .margin = margin_top, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->height, + .margin = margin_bottom, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = &usable_area->x, + .negative_axis = &usable_area->width, + .margin = margin_left, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->width, + .margin = margin_right, + }, + }; + for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { + if ((anchor & edges[i].anchors) == edges[i].anchors) { + if (edges[i].positive_axis) { + *edges[i].positive_axis += exclusive + edges[i].margin; + } + if (edges[i].negative_axis) { + *edges[i].negative_axis -= exclusive + edges[i].margin; } - surface = wlr_surface_get_root_surface(xdg_surface->popup->parent); - continue; } - - // Return early if the surface is not a layer/xdg_popup/sub surface - return NULL; - } while (true); + } } -static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, - struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) { - struct wlr_scene_node *node; - wl_list_for_each(node, &tree->children, link) { - struct sway_layer_surface *surface = scene_descriptor_try_get(node, - SWAY_SCENE_DESC_LAYER_SHELL); - // surface could be null during destruction - if (!surface) { +static void arrange_layer(struct sway_output *output, struct wl_list *list, + struct wlr_box *usable_area, bool exclusive) { + struct sway_layer_surface *sway_layer; + struct wlr_box full_area = { 0 }; + wlr_output_effective_resolution(output->wlr_output, + &full_area.width, &full_area.height); + wl_list_for_each(sway_layer, list, link) { + struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; + struct wlr_layer_surface_v1_state *state = &layer->current; + if (exclusive != (state->exclusive_zone > 0)) { continue; } - - if (!surface->scene->layer_surface->initialized) { + struct wlr_box bounds; + if (state->exclusive_zone == -1) { + bounds = full_area; + } else { + bounds = *usable_area; + } + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_height + }; + // Horizontal axis + const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + if ((state->anchor & both_horiz) && box.width == 0) { + box.x = bounds.x; + box.width = bounds.width; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x = bounds.x; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); + } + // Vertical axis + const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + if ((state->anchor & both_vert) && box.height == 0) { + box.y = bounds.y; + box.height = bounds.height; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y = bounds.y; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); + } + // Margin + if ((state->anchor & both_horiz) == both_horiz) { + box.x += state->margin.left; + box.width -= state->margin.left + state->margin.right; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x += state->margin.left; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x -= state->margin.right; + } + if ((state->anchor & both_vert) == both_vert) { + box.y += state->margin.top; + box.height -= state->margin.top + state->margin.bottom; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y += state->margin.top; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y -= state->margin.bottom; + } + if (box.width < 0 || box.height < 0) { + // TODO: Bubble up a protocol error? + wlr_layer_surface_v1_close(layer); continue; } - - if ((surface->scene->layer_surface->current.exclusive_zone > 0) != exclusive) { - continue; - } - - wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); + // Apply + sway_layer->geo = box; + apply_exclusive(usable_area, state->anchor, state->exclusive_zone, + state->margin.top, state->margin.right, + state->margin.bottom, state->margin.left); + wlr_layer_surface_v1_configure(layer, box.width, box.height); } } @@ -80,43 +162,46 @@ void arrange_layers(struct sway_output *output) { struct wlr_box usable_area = { 0 }; wlr_output_effective_resolution(output->wlr_output, &usable_area.width, &usable_area.height); - const struct wlr_box full_area = usable_area; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); + // Arrange exclusive surfaces from top->bottom + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &usable_area, true); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &usable_area, true); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &usable_area, true); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &usable_area, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); - - if (!wlr_box_equal(&usable_area, &output->usable_area)) { + if (memcmp(&usable_area, &output->usable_area, + sizeof(struct wlr_box)) != 0) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); - output->usable_area = usable_area; + memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); arrange_output(output); - } else { - arrange_popups(root->layers.popup); } + // Arrange non-exlusive surfaces from top->bottom + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &usable_area, false); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &usable_area, false); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &usable_area, false); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &usable_area, false); + // Find topmost keyboard interactive layer, if such a layer exists - struct wlr_scene_tree *layers_above_shell[] = { - output->layers.shell_overlay, - output->layers.shell_top, + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); - struct wlr_scene_node *node; - struct sway_layer_surface *topmost = NULL; + struct sway_layer_surface *layer, *topmost = NULL; for (size_t i = 0; i < nlayers; ++i) { - wl_list_for_each_reverse(node, - &layers_above_shell[i]->children, link) { - struct sway_layer_surface *surface = scene_descriptor_try_get(node, - SWAY_SCENE_DESC_LAYER_SHELL); - if (surface && surface->layer_surface->current.keyboard_interactive - == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && - surface->layer_surface->surface->mapped) { - topmost = surface; + wl_list_for_each_reverse(layer, + &output->layers[layers_above_shell[i]], link) { + if (layer->layer_surface->current.keyboard_interactive) { + topmost = layer; break; } } @@ -127,299 +212,160 @@ void arrange_layers(struct sway_output *output) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - seat->has_exclusive_layer = false; - if (topmost != NULL) { - seat_set_focus_layer(seat, topmost->layer_surface); - } else if (seat->focused_layer && - seat->focused_layer->current.keyboard_interactive - != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { - seat_set_focus_layer(seat, NULL); - } + seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL); } } -static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, - enum zwlr_layer_shell_v1_layer type) { - switch (type) { - case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: - return output->layers.shell_background; - case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: - return output->layers.shell_bottom; - case ZWLR_LAYER_SHELL_V1_LAYER_TOP: - return output->layers.shell_top; - case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: - return output->layers.shell_overlay; - } - - sway_assert(false, "unreachable"); - return NULL; -} - -static struct sway_layer_surface *sway_layer_surface_create( - struct wlr_scene_layer_surface_v1 *scene) { - struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); - if (!surface) { - sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); - return NULL; - } - - struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup); - if (!popups) { - sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node"); - free(surface); - return NULL; - } - - surface->desc.relative = &scene->tree->node; - - if (!scene_descriptor_assign(&popups->node, - SWAY_SCENE_DESC_POPUP, &surface->desc)) { - sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); - wlr_scene_node_destroy(&popups->node); - free(surface); - return NULL; - } - - surface->tree = scene->tree; - surface->scene = scene; - surface->layer_surface = scene->layer_surface; - surface->popups = popups; - surface->layer_surface->data = surface; - - return surface; -} - -static struct sway_layer_surface *find_mapped_layer_by_client( - struct wl_client *client, struct sway_output *ignore_output) { +static struct sway_layer_surface *find_any_layer_by_client( + struct wl_client *client, struct wlr_output *ignore_output) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - if (output == ignore_output) { + if (output->wlr_output == ignore_output) { continue; } // For now we'll only check the overlay layer - struct wlr_scene_node *node; - wl_list_for_each (node, &output->layers.shell_overlay->children, link) { - struct sway_layer_surface *surface = scene_descriptor_try_get(node, - SWAY_SCENE_DESC_LAYER_SHELL); - if (!surface) { - continue; - } - - struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; - struct wl_resource *resource = layer_surface->resource; - if (wl_resource_get_client(resource) == client - && layer_surface->surface->mapped) { - return surface; + struct sway_layer_surface *lsurface; + wl_list_for_each(lsurface, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { + struct wl_resource *resource = lsurface->layer_surface->resource; + if (wl_resource_get_client(resource) == client) { + return lsurface; } } } return NULL; } -static void handle_node_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *layer = - wl_container_of(listener, layer, node_destroy); - - // destroy the scene descriptor straight away if it exists, otherwise - // we will try to reflow still considering the destroyed node. - scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); - +static void handle_output_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_surface *sway_layer = + wl_container_of(listener, sway_layer, output_destroy); // Determine if this layer is being used by an exclusive client. If it is, // try and find another layer owned by this client to pass focus to. struct sway_seat *seat = input_manager_get_default_seat(); struct wl_client *client = - wl_resource_get_client(layer->layer_surface->resource); - if (!server.session_lock.lock) { - struct sway_layer_surface *consider_layer = - find_mapped_layer_by_client(client, layer->output); - if (consider_layer) { - seat_set_focus_layer(seat, consider_layer->layer_surface); + wl_resource_get_client(sway_layer->layer_surface->resource); + bool set_focus = seat->exclusive_client == client; + + wl_list_remove(&sway_layer->output_destroy.link); + wl_list_remove(&sway_layer->link); + wl_list_init(&sway_layer->link); + + if (set_focus) { + struct sway_layer_surface *layer = + find_any_layer_by_client(client, sway_layer->layer_surface->output); + if (layer) { + seat_set_focus_layer(seat, layer->layer_surface); } } - if (layer->output) { - arrange_layers(layer->output); - transaction_commit_dirty(); - } - - wlr_scene_node_destroy(&layer->popups->node); - - wl_list_remove(&layer->map.link); - wl_list_remove(&layer->unmap.link); - wl_list_remove(&layer->surface_commit.link); - wl_list_remove(&layer->node_destroy.link); - wl_list_remove(&layer->new_popup.link); - - layer->layer_surface->data = NULL; - - wl_list_remove(&layer->link); - free(layer); + sway_layer->layer_surface->output = NULL; + wlr_layer_surface_v1_close(sway_layer->layer_surface); } static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_layer_surface *surface = - wl_container_of(listener, surface, surface_commit); - - struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; - uint32_t committed = layer_surface->current.committed; - if (layer_surface->initialized && committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { - enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; - struct wlr_scene_tree *output_layer = sway_layer_get_scene( - surface->output, layer_type); - wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); + struct sway_layer_surface *layer = + wl_container_of(listener, layer, surface_commit); + struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; + struct wlr_output *wlr_output = layer_surface->output; + if (wlr_output == NULL) { + return; } - if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { - surface->mapped = layer_surface->surface->mapped; - arrange_layers(surface->output); - transaction_commit_dirty(); + struct sway_output *output = wlr_output->data; + struct wlr_box old_geo = layer->geo; + arrange_layers(output); + if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) { + output_damage_surface(output, old_geo.x, old_geo.y, + layer_surface->surface, true); + output_damage_surface(output, layer->geo.x, layer->geo.y, + layer_surface->surface, true); + } else { + output_damage_surface(output, layer->geo.x, layer->geo.y, + layer_surface->surface, false); } + + transaction_commit_dirty(); +} + +static void unmap(struct sway_layer_surface *sway_layer) { + struct wlr_output *wlr_output = sway_layer->layer_surface->output; + if (wlr_output == NULL) { + return; + } + struct sway_output *output = wlr_output->data; + if (output == NULL) { + return; + } + output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, + sway_layer->layer_surface->surface, true); + + struct sway_seat *seat = input_manager_current_seat(); + if (seat->focused_layer == sway_layer->layer_surface) { + seat_set_focus_layer(seat, NULL); + } + + cursor_rebase_all(); +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_surface *sway_layer = + wl_container_of(listener, sway_layer, destroy); + sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", + sway_layer->layer_surface->namespace); + if (sway_layer->layer_surface->mapped) { + unmap(sway_layer); + } + wl_list_remove(&sway_layer->link); + wl_list_remove(&sway_layer->destroy.link); + wl_list_remove(&sway_layer->map.link); + wl_list_remove(&sway_layer->unmap.link); + wl_list_remove(&sway_layer->surface_commit.link); + if (sway_layer->layer_surface->output != NULL) { + struct sway_output *output = sway_layer->layer_surface->output->data; + if (output != NULL) { + arrange_layers(output); + transaction_commit_dirty(); + } + wl_list_remove(&sway_layer->output_destroy.link); + sway_layer->layer_surface->output = NULL; + } + free(sway_layer); } static void handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_surface *surface = wl_container_of(listener, - surface, map); - - struct wlr_layer_surface_v1 *layer_surface = - surface->scene->layer_surface; - - // focus on new surface - if (layer_surface->current.keyboard_interactive && - (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || - layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - // but only if the currently focused layer has a lower precedence - if (!seat->focused_layer || - seat->focused_layer->current.layer >= layer_surface->current.layer) { - seat_set_focus_layer(seat, layer_surface); - } - } - arrange_layers(surface->output); - } - + struct sway_layer_surface *sway_layer = wl_container_of(listener, + sway_layer, map); + struct sway_output *output = sway_layer->layer_surface->output->data; + output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, + sway_layer->layer_surface->surface, true); + // TODO: send enter to subsurfaces and popups + wlr_surface_send_enter(sway_layer->layer_surface->surface, + sway_layer->layer_surface->output); cursor_rebase_all(); } static void handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_surface *surface = wl_container_of( - listener, surface, unmap); - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - if (seat->focused_layer == surface->layer_surface) { - seat_set_focus_layer(seat, NULL); - } - } - - cursor_rebase_all(); + struct sway_layer_surface *sway_layer = wl_container_of( + listener, sway_layer, unmap); + unmap(sway_layer); } -static void popup_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = - wl_container_of(listener, popup, destroy); - - wl_list_remove(&popup->destroy.link); - wl_list_remove(&popup->new_popup.link); - wl_list_remove(&popup->commit.link); - wl_list_remove(&popup->reposition.link); - free(popup); -} - -static void popup_unconstrain(struct sway_layer_popup *popup) { - struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; - struct sway_output *output = popup->toplevel->output; - - // if a client tries to create a popup while we are in the process of destroying - // its output, don't crash. - if (!output) { - return; - } - - int lx, ly; - wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); - - // the output box expressed in the coordinate system of the toplevel parent - // of the popup - struct wlr_box output_toplevel_sx_box = { - .x = output->lx - lx, - .y = output->ly - ly, - .width = output->width, - .height = output->height, - }; - - wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); -} - -static void popup_handle_commit(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); - if (popup->wlr_popup->base->initial_commit) { - popup_unconstrain(popup); - } -} - -static void popup_handle_reposition(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, reposition); - popup_unconstrain(popup); -} - -static void popup_handle_new_popup(struct wl_listener *listener, void *data); - -static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, - struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { - struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); - if (popup == NULL) { - return NULL; - } - - popup->toplevel = toplevel; - popup->wlr_popup = wlr_popup; - popup->scene = wlr_scene_xdg_surface_create(parent, - wlr_popup->base); - - if (!popup->scene) { - free(popup); - return NULL; - } - - popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); - popup->new_popup.notify = popup_handle_new_popup; - wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); - popup->commit.notify = popup_handle_commit; - wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); - popup->reposition.notify = popup_handle_reposition; - wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); - - return popup; -} - -static void popup_handle_new_popup(struct wl_listener *listener, void *data) { - struct sway_layer_popup *sway_layer_popup = - wl_container_of(listener, sway_layer_popup, new_popup); - struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); -} - -static void handle_new_popup(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer_surface = - wl_container_of(listener, sway_layer_surface, new_popup); - struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); +struct sway_layer_surface *layer_from_wlr_layer_surface_v1( + struct wlr_layer_surface_v1 *layer_surface) { + return layer_surface->data; } void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct wlr_layer_surface_v1 *layer_surface = data; - sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 - " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", - layer_surface->namespace, - layer_surface->pending.layer, - layer_surface->pending.anchor, - layer_surface->pending.desired_width, - layer_surface->pending.desired_height, - layer_surface->pending.margin.top, - layer_surface->pending.margin.right, - layer_surface->pending.margin.bottom, - layer_surface->pending.margin.left); + sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %d " + "size %dx%d margin %d,%d,%d,%d", + layer_surface->namespace, layer_surface->layer, layer_surface->layer, + layer_surface->client_pending.desired_width, + layer_surface->client_pending.desired_height, + layer_surface->client_pending.margin.top, + layer_surface->client_pending.margin.right, + layer_surface->client_pending.margin.bottom, + layer_surface->client_pending.margin.left); if (!layer_surface->output) { // Assign last active output @@ -427,16 +373,17 @@ 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 || output == root->fallback_output) { + if (!output) { if (!root->outputs->length) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", layer_surface->namespace); - wlr_layer_surface_v1_destroy(layer_surface); + wlr_layer_surface_v1_close(layer_surface); return; } output = root->outputs->items[0]; @@ -444,63 +391,37 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->output = output->wlr_output; } - struct sway_output *output = layer_surface->output->data; - - enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; - struct wlr_scene_tree *output_layer = sway_layer_get_scene( - output, layer_type); - struct wlr_scene_layer_surface_v1 *scene_surface = - wlr_scene_layer_surface_v1_create(output_layer, layer_surface); - if (!scene_surface) { - sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1"); + struct sway_layer_surface *sway_layer = + calloc(1, sizeof(struct sway_layer_surface)); + if (!sway_layer) { return; } - struct sway_layer_surface *surface = - sway_layer_surface_create(scene_surface); - if (!surface) { - wlr_layer_surface_v1_destroy(layer_surface); - - sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); - return; - } - - if (!scene_descriptor_assign(&scene_surface->tree->node, - SWAY_SCENE_DESC_LAYER_SHELL, surface)) { - sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); - // destroying the layer_surface will also destroy its corresponding - // scene node - wlr_layer_surface_v1_destroy(layer_surface); - return; - } - - surface->output = output; - wl_list_insert(&output->layer_surfaces, &surface->link); - - // now that the surface's output is known, we can advertise its scale - wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface, - layer_surface->output->scale); - wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface, - ceil(layer_surface->output->scale)); - - surface->surface_commit.notify = handle_surface_commit; + sway_layer->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, - &surface->surface_commit); - surface->map.notify = handle_map; - wl_signal_add(&layer_surface->surface->events.map, &surface->map); - surface->unmap.notify = handle_unmap; - wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); - surface->new_popup.notify = handle_new_popup; - wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); + &sway_layer->surface_commit); - surface->node_destroy.notify = handle_node_destroy; - wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); -} + sway_layer->destroy.notify = handle_destroy; + wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); + sway_layer->map.notify = handle_map; + wl_signal_add(&layer_surface->events.map, &sway_layer->map); + sway_layer->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); + // TODO: Listen for subsurfaces -void destroy_layers(struct sway_output *output) { - struct sway_layer_surface *layer, *layer_tmp; - wl_list_for_each_safe(layer, layer_tmp, &output->layer_surfaces, link) { - layer->output = NULL; - wlr_layer_surface_v1_destroy(layer->layer_surface); - } + sway_layer->layer_surface = layer_surface; + layer_surface->data = sway_layer; + + struct sway_output *output = layer_surface->output->data; + sway_layer->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); + + wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->link); + + // Temporarily set the layer's current state to client_pending + // So that we can easily arrange it + struct wlr_layer_surface_v1_state old_state = layer_surface->current; + layer_surface->current = layer_surface->client_pending; + arrange_layers(output); + layer_surface->current = old_state; } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 12dc9cc7a..c5461ee66 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1,33 +1,27 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include #include -#include -#include -#include -#include +#include #include +#include #include -#include -#include +#include +#include #include -#include -#include #include #include -#include +#include #include -#include #include "config.h" #include "log.h" #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" -#include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" -#include "sway/scene_descriptor.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" @@ -35,693 +29,598 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" -#if WLR_HAS_DRM_BACKEND -#include -#include -#endif - -bool output_match_name_or_id(struct sway_output *output, - const char *name_or_id) { - if (strcmp(name_or_id, "*") == 0) { - return true; - } - - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - return strcasecmp(identifier, name_or_id) == 0 - || strcasecmp(output->wlr_output->name, name_or_id) == 0; -} - struct sway_output *output_by_name_or_id(const char *name_or_id) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - if (output_match_name_or_id(output, name_or_id)) { + 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; } -struct sway_output *all_output_by_name_or_id(const char *name_or_id) { +/** + * 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). + */ +static void rotate_child_position(double *sx, double *sy, double sw, double sh, + double pw, double ph, float rotation) { + if (rotation == 0.0f) { + return; + } + + // Coordinates relative to the center of the subsurface + double ox = *sx - pw/2 + sw/2, + oy = *sy - ph/2 + sh/2; + // Rotated coordinates + double rx = cos(-rotation)*ox - sin(-rotation)*oy, + ry = cos(-rotation)*oy + sin(-rotation)*ox; + *sx = rx + pw/2 - sw/2; + *sy = ry + ph/2 - sh/2; +} + +struct surface_iterator_data { + sway_surface_iterator_func_t user_iterator; + void *user_data; + struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - if (output_match_name_or_id(output, name_or_id)) { - return output; - } + double ox, oy; + int width, height; + float rotation; +}; + +static bool get_surface_box(struct surface_iterator_data *data, + struct wlr_surface *surface, int sx, int sy, + struct wlr_box *surface_box) { + struct sway_output *output = data->output; + + if (!wlr_surface_has_buffer(surface)) { + return false; } - return NULL; + + int sw = surface->current.width; + int sh = surface->current.height; + + double _sx = sx + surface->sx; + double _sy = sy + surface->sy; + rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height, + data->rotation); + + struct wlr_box box = { + .x = data->ox + _sx, + .y = data->oy + _sy, + .width = sw, + .height = sh, + }; + if (surface_box != NULL) { + memcpy(surface_box, &box, sizeof(struct wlr_box)); + } + + struct wlr_box rotated_box; + wlr_box_rotated_bounds(&rotated_box, &box, data->rotation); + + struct wlr_box output_box = { + .width = output->width, + .height = output->height, + }; + + struct wlr_box intersection; + return wlr_box_intersection(&intersection, &output_box, &rotated_box); } +static void output_for_each_surface_iterator(struct wlr_surface *surface, + int sx, int sy, void *_data) { + struct surface_iterator_data *data = _data; + + struct wlr_box box; + bool intersects = get_surface_box(data, surface, sx, sy, &box); + if (!intersects) { + return; + } + + data->user_iterator(data->output, surface, &box, data->rotation, + data->user_data); +} + +void output_surface_for_each_surface(struct sway_output *output, + struct wlr_surface *surface, double ox, double oy, + sway_surface_iterator_func_t iterator, void *user_data) { + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .ox = ox, + .oy = oy, + .width = surface->current.width, + .height = surface->current.height, + .rotation = 0, + }; + + wlr_surface_for_each_surface(surface, + output_for_each_surface_iterator, &data); +} + +void output_view_for_each_surface(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data) { + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .ox = view->container->current.content_x - output->wlr_output->lx + - view->geometry.x, + .oy = view->container->current.content_y - output->wlr_output->ly + - view->geometry.y, + .width = view->container->current.content_width, + .height = view->container->current.content_height, + .rotation = 0, // TODO + }; + + view_for_each_surface(view, output_for_each_surface_iterator, &data); +} + +void output_view_for_each_popup(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data) { + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .ox = view->container->current.content_x - output->wlr_output->lx + - view->geometry.x, + .oy = view->container->current.content_y - output->wlr_output->ly + - view->geometry.y, + .width = view->container->current.content_width, + .height = view->container->current.content_height, + .rotation = 0, // TODO + }; + + view_for_each_popup(view, output_for_each_surface_iterator, &data); +} + +void output_layer_for_each_surface(struct sway_output *output, + struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, + void *user_data) { + struct sway_layer_surface *layer_surface; + wl_list_for_each(layer_surface, layer_surfaces, link) { + struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = + layer_surface->layer_surface; + output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, + layer_surface->geo.x, layer_surface->geo.y, iterator, + user_data); + } +} + +#if HAVE_XWAYLAND +void output_unmanaged_for_each_surface(struct sway_output *output, + struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, + void *user_data) { + struct sway_xwayland_unmanaged *unmanaged_surface; + 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; + + output_surface_for_each_surface(output, xsurface->surface, ox, oy, + iterator, user_data); + } +} +#endif + +void output_drag_icons_for_each_surface(struct sway_output *output, + struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, + 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; + + if (drag_icon->wlr_drag_icon->mapped) { + output_surface_for_each_surface(output, + drag_icon->wlr_drag_icon->surface, ox, oy, + iterator, user_data); + } + } +} + +static void for_each_surface_container_iterator(struct sway_container *con, + void *_data) { + if (!con->view || !view_is_visible(con->view)) { + return; + } + + struct surface_iterator_data *data = _data; + output_view_for_each_surface(data->output, con->view, + data->user_iterator, data->user_data); +} + +static void output_for_each_surface(struct sway_output *output, + sway_surface_iterator_func_t iterator, void *user_data) { + if (output_has_opaque_overlay_layer_surface(output)) { + goto overlay; + } + + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + }; + + struct sway_workspace *workspace = output_get_active_workspace(output); + 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; + } + if (fullscreen_con) { + for_each_surface_container_iterator(fullscreen_con, &data); + container_for_each_child(fullscreen_con, + for_each_surface_container_iterator, &data); + + // TODO: Show transient containers for fullscreen global + if (fullscreen_con == workspace->current.fullscreen) { + for (int i = 0; i < workspace->current.floating->length; ++i) { + struct sway_container *floater = + workspace->current.floating->items[i]; + if (container_is_transient_for(floater, fullscreen_con)) { + for_each_surface_container_iterator(floater, &data); + } + } + } +#if HAVE_XWAYLAND + output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, + iterator, user_data); +#endif + } else { + output_layer_for_each_surface(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + iterator, user_data); + output_layer_for_each_surface(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + iterator, user_data); + + workspace_for_each_container(workspace, + for_each_surface_container_iterator, &data); + +#if HAVE_XWAYLAND + output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, + iterator, user_data); +#endif + output_layer_for_each_surface(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + iterator, user_data); + } + +overlay: + output_layer_for_each_surface(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + iterator, user_data); + output_drag_icons_for_each_surface(output, &root->drag_icons, + iterator, user_data); +} + +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +void scale_box(struct wlr_box *box, float scale) { + box->width = scale_length(box->width, box->x, scale); + box->height = scale_length(box->height, box->y, scale); + box->x = round(box->x * scale); + box->y = round(box->y * scale); +} struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); if (!focus) { - if (!output->workspaces->length) { - return NULL; - } return output->workspaces->items[0]; } return focus->sway_workspace; } -struct send_frame_done_data { - struct timespec when; - int msec_until_refresh; - struct sway_output *output; -}; - -struct buffer_timer { - struct wl_listener destroy; - struct wl_event_source *frame_done_timer; -}; - -static int handle_buffer_timer(void *data) { - struct wlr_scene_surface *scene_surface = data; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_surface_send_frame_done(scene_surface, &now); - return 0; -} - -static void handle_buffer_timer_destroy(struct wl_listener *listener, - void *data) { - struct buffer_timer *timer = wl_container_of(listener, timer, destroy); - - wl_list_remove(&timer->destroy.link); - wl_event_source_remove(timer->frame_done_timer); - free(timer); -} - -static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_surface *scene_surface) { - struct wlr_scene_buffer *buffer = scene_surface->buffer; - - struct buffer_timer *timer = - scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); - if (timer) { - return timer; - } - - timer = calloc(1, sizeof(struct buffer_timer)); - if (!timer) { - return NULL; - } - - timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, - handle_buffer_timer, scene_surface); - if (!timer->frame_done_timer) { - free(timer); - return NULL; - } - - scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); - - timer->destroy.notify = handle_buffer_timer_destroy; - wl_signal_add(&buffer->node.events.destroy, &timer->destroy); - - return timer; -} - -static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, - int x, int y, void *user_data) { - struct send_frame_done_data *data = user_data; - struct sway_output *output = data->output; - int view_max_render_time = 0; - - if (buffer->primary_output != data->output->scene_output) { - return; - } - - struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer); - if (scene_surface == NULL) { - return; - } - - struct wlr_scene_node *current = &buffer->node; - while (true) { - struct sway_view *view = scene_descriptor_try_get(current, - SWAY_SCENE_DESC_VIEW); - if (view) { - view_max_render_time = view->max_render_time; - break; +bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { + struct wlr_layer_surface_v1 *wlr_layer_surface_v1; + wl_list_for_each(wlr_layer_surface_v1, &server.layer_shell->surfaces, link) { + if (wlr_layer_surface_v1->output != output->wlr_output || + wlr_layer_surface_v1->layer != ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + continue; } - - if (!current->parent) { - break; - } - - current = ¤t->parent->node; - } - - int delay = data->msec_until_refresh - output->max_render_time - - view_max_render_time; - - struct buffer_timer *timer = NULL; - - if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { - timer = buffer_timer_get_or_create(scene_surface); - } - - if (timer) { - wl_event_source_timer_update(timer->frame_done_timer, delay); - } else { - wlr_scene_surface_send_frame_done(scene_surface, &data->when); - } -} - -static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, - struct wlr_scene_buffer *buffer) { - // if we are scaling down, we should always choose linear - if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( - buffer->dst_width < buffer->WLR_PRIVATE.buffer_width || - buffer->dst_height < buffer->WLR_PRIVATE.buffer_height)) { - return WLR_SCALE_FILTER_BILINEAR; - } - - switch (output->scale_filter) { - case SCALE_FILTER_LINEAR: - return WLR_SCALE_FILTER_BILINEAR; - case SCALE_FILTER_NEAREST: - return WLR_SCALE_FILTER_NEAREST; - default: - abort(); // unreachable - } -} - -void output_configure_scene(struct sway_output *output, - struct wlr_scene_node *node, float opacity) { - if (!node->enabled) { - return; - } - - struct sway_container *con = - scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); - if (con) { - opacity = con->alpha; - } - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(buffer); - - if (surface) { - const struct wlr_alpha_modifier_surface_v1_state *alpha_modifier_state = - wlr_alpha_modifier_v1_get_surface_state(surface->surface); - if (alpha_modifier_state != NULL) { - opacity *= (float)alpha_modifier_state->multiplier; - } - } - - // hack: don't call the scene setter because that will damage all outputs - // We don't want to damage outputs that aren't our current output that - // we're configuring - if (output) { - buffer->filter_mode = get_scale_filter(output, buffer); - } - - wlr_scene_buffer_set_opacity(buffer, opacity); - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *node; - wl_list_for_each(node, &tree->children, link) { - output_configure_scene(output, node, opacity); + struct wlr_surface *wlr_surface = wlr_layer_surface_v1->surface; + struct sway_layer_surface *sway_layer_surface = + layer_from_wlr_layer_surface_v1(wlr_layer_surface_v1); + pixman_box32_t output_box = { + .x2 = output->width, + .y2 = output->height, + }; + pixman_region32_t surface_opaque_box; + pixman_region32_init(&surface_opaque_box); + pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); + pixman_region32_translate(&surface_opaque_box, + sway_layer_surface->geo.x, sway_layer_surface->geo.y); + bool contains = pixman_region32_contains_rectangle(&surface_opaque_box, + &output_box); + pixman_region32_fini(&surface_opaque_box); + if (contains) { + return true; } } -} - -static bool output_can_tear(struct sway_output *output) { - struct sway_workspace *workspace = output->current.active_workspace; - if (!workspace) { - return false; - } - - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - fullscreen_con = workspace->current.fullscreen; - } - if (fullscreen_con && fullscreen_con->view) { - return (output->allow_tearing && view_can_tear(fullscreen_con->view)); - } - return false; } -static int output_repaint_timer_handler(void *data) { - struct sway_output *output = data; - - output->wlr_output->frame_pending = false; - if (!output->wlr_output->enabled) { - return 0; - } - - output_configure_scene(output, &root->root_scene->tree.node, 1.0f); - - struct wlr_scene_output_state_options opts = { - .color_transform = output->color_transform, - }; - - struct wlr_scene_output *scene_output = output->scene_output; - if (!wlr_scene_output_needs_frame(scene_output)) { - return 0; - } - - struct wlr_output_state pending; - wlr_output_state_init(&pending); - if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { - wlr_output_state_finish(&pending); - return 0; - } - - if (output_can_tear(output)) { - pending.tearing_page_flip = true; - - if (!wlr_output_test_state(output->wlr_output, &pending)) { - sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip", - output->wlr_output->name); - pending.tearing_page_flip = false; - } - } - - if (!wlr_output_commit_state(output->wlr_output, &pending)) { - sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name); - } - wlr_output_state_finish(&pending); - return 0; +static void send_frame_done_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *data) { + struct timespec *when = data; + wlr_surface_send_frame_done(surface, when); } -static void handle_frame(struct wl_listener *listener, void *user_data) { +static void send_frame_done(struct sway_output *output, struct timespec *when) { + output_for_each_surface(output, send_frame_done_iterator, when); +} + +static void damage_handle_frame(struct wl_listener *listener, void *data) { struct sway_output *output = - wl_container_of(listener, output, frame); + wl_container_of(listener, output, damage_frame); if (!output->enabled || !output->wlr_output->enabled) { return; } - // Compute predicted milliseconds until the next refresh. It's used for - // delaying both output rendering and surface frame callbacks. - int msec_until_refresh = 0; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - if (output->max_render_time != 0) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - const long NSEC_IN_SECONDS = 1000000000; - struct timespec predicted_refresh = output->last_presentation; - predicted_refresh.tv_nsec += output->refresh_nsec % NSEC_IN_SECONDS; - predicted_refresh.tv_sec += output->refresh_nsec / NSEC_IN_SECONDS; - if (predicted_refresh.tv_nsec >= NSEC_IN_SECONDS) { - predicted_refresh.tv_sec += 1; - predicted_refresh.tv_nsec -= NSEC_IN_SECONDS; - } - - // If the predicted refresh time is before the current time then - // there's no point in delaying. - // - // We only check tv_sec because if the predicted refresh time is less - // than a second before the current time, then msec_until_refresh will - // end up slightly below zero, which will effectively disable the delay - // without potential disastrous negative overflows that could occur if - // tv_sec was not checked. - if (predicted_refresh.tv_sec >= now.tv_sec) { - long nsec_until_refresh - = (predicted_refresh.tv_sec - now.tv_sec) * NSEC_IN_SECONDS - + (predicted_refresh.tv_nsec - now.tv_nsec); - - // We want msec_until_refresh to be conservative, that is, floored. - // If we have 7.9 msec until refresh, we better compute the delay - // as if we had only 7 msec, so that we don't accidentally delay - // more than necessary and miss a frame. - msec_until_refresh = nsec_until_refresh / 1000000; - } + bool needs_swap; + pixman_region32_t damage; + pixman_region32_init(&damage); + if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) { + return; } - int delay = msec_until_refresh - output->max_render_time; - - // If the delay is less than 1 millisecond (which is the least we can wait) - // then just render right away. - if (delay < 1) { - output_repaint_timer_handler(output); - } else { - output->wlr_output->frame_pending = true; - wl_event_source_timer_update(output->repaint_timer, delay); + if (needs_swap) { + output_render(output, &now, &damage); } + pixman_region32_fini(&damage); + // Send frame done to all visible surfaces - struct send_frame_done_data data = {0}; - clock_gettime(CLOCK_MONOTONIC, &data.when); - data.msec_until_refresh = msec_until_refresh; - data.output = output; - wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); + send_frame_done(output, &now); } -void update_output_manager_config(struct sway_server *server) { - struct wlr_output_configuration_v1 *config = - wlr_output_configuration_v1_create(); +void output_damage_whole(struct sway_output *output) { + // The output can exist with no wlr_output if it's just been disconnected + // and the transaction to evacuate it has't completed yet. + if (output && output->wlr_output && output->damage) { + wlr_output_damage_add_whole(output->damage); + } +} - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - if (output == root->fallback_output) { - continue; +static void damage_surface_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *_box, float rotation, + void *_data) { + bool *data = _data; + bool whole = *data; + + struct wlr_box box = *_box; + scale_box(&box, output->wlr_output->scale); + + int center_x = box.x + box.width/2; + int center_y = box.y + box.height/2; + + if (pixman_region32_not_empty(&surface->buffer_damage)) { + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_surface_get_effective_damage(surface, &damage); + wlr_region_scale(&damage, &damage, output->wlr_output->scale); + if (ceil(output->wlr_output->scale) > surface->current.scale) { + // When scaling up a surface, it'll become blurry so we need to + // expand the damage region + wlr_region_expand(&damage, &damage, + ceil(output->wlr_output->scale) - surface->current.scale); } - 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, &output_box); - // We mark the output enabled when it's switched off but not disabled - config_head->state.enabled = !wlr_box_empty(&output_box); - config_head->state.x = output_box.x; - config_head->state.y = output_box.y; + pixman_region32_translate(&damage, box.x, box.y); + wlr_region_rotated_bounds(&damage, &damage, rotation, + center_x, center_y); + wlr_output_damage_add(output->damage, &damage); + pixman_region32_fini(&damage); } - wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); - - ipc_event_output(); -} - -static int timer_modeset_handle(void *data) { - struct sway_server *server = data; - wl_event_source_remove(server->delayed_modeset); - server->delayed_modeset = NULL; - - apply_stored_output_configs(); - return 0; -} - -void request_modeset(void) { - if (server.delayed_modeset == NULL) { - server.delayed_modeset = wl_event_loop_add_timer(server.wl_event_loop, - timer_modeset_handle, &server); - wl_event_source_timer_update(server.delayed_modeset, 10); + if (whole) { + wlr_box_rotated_bounds(&box, &box, rotation); + wlr_output_damage_add_box(output->damage, &box); } + + wlr_output_schedule_frame(output->wlr_output); } -bool modeset_is_pending(void) { - return server.delayed_modeset != NULL; +void output_damage_surface(struct sway_output *output, double ox, double oy, + struct wlr_surface *surface, bool whole) { + output_surface_for_each_surface(output, surface, ox, oy, + damage_surface_iterator, &whole); } -void force_modeset(void) { - if (server.delayed_modeset != NULL) { - wl_event_source_remove(server.delayed_modeset); - server.delayed_modeset = NULL; +static void output_damage_view(struct sway_output *output, + struct sway_view *view, bool whole) { + if (!view_is_visible(view)) { + return; } - apply_stored_output_configs(); + output_view_for_each_surface(output, view, damage_surface_iterator, &whole); } -static void begin_destroy(struct sway_output *output) { +void output_damage_from_view(struct sway_output *output, + struct sway_view *view) { + output_damage_view(output, view, false); +} - wl_list_remove(&output->layout_destroy.link); - wl_list_remove(&output->destroy.link); - wl_list_remove(&output->present.link); - wl_list_remove(&output->frame.link); - wl_list_remove(&output->request_state.link); +// 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; + scale_box(&box, output->wlr_output->scale); + wlr_output_damage_add_box(output->damage, &box); +} - // Remove the scene_output first to ensure that the scene does not emit - // events for this output. - wlr_scene_output_destroy(output->scene_output); - output->scene_output = NULL; +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, + .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); +} + +static void damage_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = + wl_container_of(listener, output, damage_destroy); + if (!output->enabled) { + return; + } + output_disable(output); + transaction_commit_dirty(); +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, destroy); + wl_signal_emit(&output->events.destroy, output); if (output->enabled) { output_disable(output); } output_begin_destroy(output); - wl_list_remove(&output->link); - output->wlr_output->data = NULL; - output->wlr_output = NULL; + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->mode.link); + wl_list_remove(&output->transform.link); + wl_list_remove(&output->scale.link); + 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); - wl_event_source_remove(output->repaint_timer); - output->repaint_timer = NULL; - - request_modeset(); + transaction_commit_dirty(); } -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, destroy); - begin_destroy(output); +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); + if (output->wlr_output->current_mode != NULL && + (!oc || oc->enabled)) { + // We want to enable this output, but it didn't work last time, + // possibly because we hadn't enough CRTCs. Try again now that the + // output has a mode. + sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " + "trying to enable it", output->wlr_output->name); + apply_output_config(oc, output); + } + return; + } + if (!output->enabled || !output->configured) { + return; + } + arrange_layers(output); + arrange_output(output); + transaction_commit_dirty(); } -static void handle_layout_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, layout_destroy); - begin_destroy(output); +static void handle_transform(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, transform); + if (!output->enabled || !output->configured) { + return; + } + arrange_layers(output); + arrange_output(output); + transaction_commit_dirty(); +} + +static void update_textures(struct sway_container *con, void *data) { + container_update_title_textures(con); + container_update_marks_textures(con); +} + +static void handle_scale(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, scale); + if (!output->enabled || !output->configured) { + return; + } + arrange_layers(output); + output_for_each_container(output, update_textures, NULL); + arrange_output(output); + transaction_commit_dirty(); +} + +static void send_presented_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *data) { + struct wlr_presentation_event *event = data; + wlr_presentation_send_surface_presented(server.presentation, + surface, event); } static void handle_present(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, present); struct wlr_output_event_present *output_event = data; - if (!output->enabled || !output_event->presented) { + if (!output->enabled) { return; } - output->last_presentation = output_event->when; - output->refresh_nsec = output_event->refresh; + struct wlr_presentation_event event = { + .output = output->wlr_output, + .tv_sec = (uint64_t)output_event->when->tv_sec, + .tv_nsec = (uint32_t)output_event->when->tv_nsec, + .refresh = (uint32_t)output_event->refresh, + .seq = (uint64_t)output_event->seq, + .flags = output_event->flags, + }; + output_for_each_surface(output, send_presented_iterator, &event); } -static void handle_request_state(struct wl_listener *listener, void *data) { - struct sway_output *output = - wl_container_of(listener, output, request_state); - const struct wlr_output_event_request_state *event = data; - const struct wlr_output_state *state = event->state; - - // Store the requested changes so that the active configuration is - // consistent with the current state, and to avoid duplicate logic to apply - // the changes. - struct output_config *oc = new_output_config(output->wlr_output->name); - if (!oc) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - - int committed = state->committed; - if (committed & WLR_OUTPUT_STATE_MODE) { - if (state->mode != NULL) { - oc->width = state->mode->width; - oc->height = state->mode->height; - oc->refresh_rate = state->mode->refresh / 1000.f; - } else { - oc->width = state->custom_mode.width; - oc->height = state->custom_mode.height; - oc->refresh_rate = state->custom_mode.refresh / 1000.f; - } - committed &= ~WLR_OUTPUT_STATE_MODE; - } - if (committed & WLR_OUTPUT_STATE_SCALE) { - oc->scale = state->scale; - committed &= ~WLR_OUTPUT_STATE_SCALE; - } - if (committed & WLR_OUTPUT_STATE_TRANSFORM) { - oc->transform = state->transform; - committed &= ~WLR_OUTPUT_STATE_TRANSFORM; - } - - // We do not expect or support any other changes here - assert(committed == 0); - store_output_config(oc); - - force_modeset(); -} - -static unsigned int last_headless_num = 0; - void handle_new_output(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; - - if (wlr_output == root->fallback_output->wlr_output) { - return; - } - - if (wlr_output_is_headless(wlr_output)) { - char name[64]; - snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); - wlr_output_set_name(wlr_output, name); - } - - sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", - wlr_output, wlr_output->name, wlr_output->non_desktop); - - if (wlr_output->non_desktop) { - sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); - struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); -#if WLR_HAS_DRM_BACKEND - if (server->drm_lease_manager) { - wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, - wlr_output); - } -#endif - list_add(root->non_desktop_outputs, non_desktop); - return; - } - - if (!wlr_output_init_render(wlr_output, server->allocator, - server->renderer)) { - sway_log(SWAY_ERROR, "Failed to init output render"); - return; - } - - // Create the scene output here so we're not accidentally creating one for - // the fallback output - struct wlr_scene_output *scene_output = - wlr_scene_output_create(root->root_scene, wlr_output); - if (!scene_output) { - sway_log(SWAY_ERROR, "Failed to create a scene output"); - return; - } + sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); struct sway_output *output = output_create(wlr_output); if (!output) { - sway_log(SWAY_ERROR, "Failed to create a sway output"); - wlr_scene_output_destroy(scene_output); return; } - output->server = server; - output->scene_output = scene_output; + output->damage = wlr_output_damage_create(wlr_output); - wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); - output->layout_destroy.notify = handle_layout_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; + wl_signal_add(&wlr_output->events.mode, &output->mode); + output->mode.notify = handle_mode; + wl_signal_add(&wlr_output->events.transform, &output->transform); + output->transform.notify = handle_transform; + wl_signal_add(&wlr_output->events.scale, &output->scale); + output->scale.notify = handle_scale; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&wlr_output->events.frame, &output->frame); - output->frame.notify = handle_frame; - wl_signal_add(&wlr_output->events.request_state, &output->request_state); - output->request_state.notify = handle_request_state; + wl_signal_add(&output->damage->events.frame, &output->damage_frame); + output->damage_frame.notify = damage_handle_frame; + wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); + output->damage_destroy.notify = damage_handle_destroy; + wl_list_init(&output->swaybg_client_destroy.link); - output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, - output_repaint_timer_handler, output); - - if (server->session_lock.lock) { - sway_session_lock_add_output(server->session_lock.lock, output); - } - - request_modeset(); -} - -static struct output_config *output_config_for_config_head( - struct wlr_output_configuration_head_v1 *config_head) { - struct output_config *oc = new_output_config(config_head->state.output->name); - if (!oc) { - return NULL; - } - - oc->enabled = config_head->state.enabled; - if (!oc->enabled) { - return oc; - } - - 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 / 1000.f; + struct output_config *oc = output_find_config(output); + if (!oc || oc->enabled) { + output_enable(output, oc); } 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 / 1000.f; + wlr_output_enable(output->wlr_output, false); } - 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->adaptive_sync = config_head->state.adaptive_sync_enabled; - return oc; -} - -static void output_manager_apply(struct sway_server *server, - struct wlr_output_configuration_v1 *cfg, bool test_only) { - bool ok = false; - size_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads); - struct output_config **configs = calloc(configs_len, sizeof(*configs)); - if (!configs) { - sway_log(SWAY_ERROR, "Allocation failed"); - goto error; - } - size_t start_new_configs = config->output_configs->length; - for (size_t idx = 0; idx < start_new_configs; idx++) { - configs[idx] = config->output_configs->items[idx]; - } - - size_t config_idx = start_new_configs; - struct wlr_output_configuration_head_v1 *config_head; - wl_list_for_each(config_head, &cfg->heads, link) { - // Generate the configuration and store it as a temporary - // config. We keep a record of it so we can remove it later. - struct output_config *oc = output_config_for_config_head(config_head); - if (!oc) { - sway_log(SWAY_ERROR, "Allocation failed"); - goto error_config; - } - configs[config_idx++] = oc; - } - - // Try to commit without degrade to off enabled. Note that this will fail - // if any output configured for enablement fails to be enabled, even if it - // was not part of the config heads we were asked to configure. - ok = apply_output_configs(configs, configs_len, test_only, false); - -error_config: - for (size_t idx = start_new_configs; idx < configs_len; idx++) { - struct output_config *cfg = configs[idx]; - if (!test_only && ok) { - store_output_config(cfg); - } else { - free_output_config(cfg); - } - } - free(configs); - -error: - if (ok) { - wlr_output_configuration_v1_send_succeeded(cfg); - if (server->delayed_modeset != NULL) { - wl_event_source_remove(server->delayed_modeset); - server->delayed_modeset = NULL; - } - } else { - wlr_output_configuration_v1_send_failed(cfg); - } - wlr_output_configuration_v1_destroy(cfg); -} - -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; - - output_manager_apply(server, config, false); -} - -void handle_output_manager_test(struct wl_listener *listener, void *data) { - struct sway_server *server = - wl_container_of(listener, server, output_manager_test); - struct wlr_output_configuration_v1 *config = data; - - output_manager_apply(server, config, true); -} - -void handle_output_power_manager_set_mode(struct wl_listener *listener, - void *data) { - struct wlr_output_power_v1_set_mode_event *event = data; - struct sway_output *output = event->output->data; - - struct output_config *oc = new_output_config(output->wlr_output->name); - if (!oc) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - - switch (event->mode) { - case ZWLR_OUTPUT_POWER_V1_MODE_OFF: - oc->power = 0; - break; - case ZWLR_OUTPUT_POWER_V1_MODE_ON: - oc->power = 1; - break; - } - store_output_config(oc); - request_modeset(); + + transaction_commit_dirty(); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c new file mode 100644 index 000000000..92e623ef4 --- /dev/null +++ b/sway/desktop/render.c @@ -0,0 +1,1091 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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" +#include "sway/output.h" +#include "sway/server.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/root.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" + +struct render_data { + pixman_region32_t *damage; + float alpha; +}; + +/** + * Apply scale to a width or height. + * + * One does not simply multiply the width by the scale. We allow fractional + * scaling, which means the resulting scaled width might be a decimal. + * So we round it. + * + * But even this can produce undesirable results depending on the X or Y offset + * of the box. For example, with a scale of 1.5, a box with width=1 should not + * scale to 2px if its X coordinate is 1, because the X coordinate would have + * scaled to 2px. + */ +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +static void scissor_output(struct wlr_output *wlr_output, + pixman_box32_t *rect) { + struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); + assert(renderer); + + struct wlr_box box = { + .x = rect->x1, + .y = rect->y1, + .width = rect->x2 - rect->x1, + .height = rect->y2 - rect->y1, + }; + + int ow, oh; + wlr_output_transformed_resolution(wlr_output, &ow, &oh); + + enum wl_output_transform transform = + wlr_output_transform_invert(wlr_output->transform); + wlr_box_transform(&box, &box, transform, ow, oh); + + wlr_renderer_scissor(renderer, &box); +} + +static void render_texture(struct wlr_output *wlr_output, + pixman_region32_t *output_damage, struct wlr_texture *texture, + const struct wlr_box *box, const float matrix[static 9], float alpha) { + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect(&damage, &damage, box->x, box->y, + box->width, box->height); + pixman_region32_intersect(&damage, &damage, output_damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto damage_finish; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); + } + +damage_finish: + pixman_region32_fini(&damage); +} + +static void render_surface_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *_box, float rotation, + void *_data) { + struct render_data *data = _data; + struct wlr_output *wlr_output = output->wlr_output; + pixman_region32_t *output_damage = data->damage; + float alpha = data->alpha; + + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (!texture) { + return; + } + + struct wlr_box box = *_box; + scale_box(&box, wlr_output->scale); + + float matrix[9]; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + wlr_matrix_project_box(matrix, &box, transform, rotation, + wlr_output->transform_matrix); + + render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); +} + +static void render_layer(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *layer_surfaces) { + struct render_data data = { + .damage = damage, + .alpha = 1.0f, + }; + output_layer_for_each_surface(output, layer_surfaces, + render_surface_iterator, &data); +} + +#if HAVE_XWAYLAND +static void render_unmanaged(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *unmanaged) { + struct render_data data = { + .damage = damage, + .alpha = 1.0f, + }; + output_unmanaged_for_each_surface(output, unmanaged, + render_surface_iterator, &data); +} +#endif + +static void render_drag_icons(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *drag_icons) { + struct render_data data = { + .damage = damage, + .alpha = 1.0f, + }; + output_drag_icons_for_each_surface(output, drag_icons, + render_surface_iterator, &data); +} + +// _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, + pixman_region32_t *output_damage, const struct wlr_box *_box, + float color[static 4]) { + 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; + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect(&damage, &damage, box.x, box.y, + box.width, box.height); + pixman_region32_intersect(&damage, &damage, output_damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto damage_finish; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_render_rect(renderer, &box, color, + wlr_output->transform_matrix); + } + +damage_finish: + pixman_region32_fini(&damage); +} + +void premultiply_alpha(float color[4], float opacity) { + color[3] *= opacity; + color[0] *= color[3]; + color[1] *= color[3]; + color[2] *= color[3]; +} + +static void render_view_toplevels(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct render_data data = { + .damage = damage, + .alpha = alpha, + }; + // Render all toplevels without descending into popups + double ox = view->container->surface_x - + output->wlr_output->lx - view->geometry.x; + double oy = view->container->surface_y - + output->wlr_output->ly - view->geometry.y; + output_surface_for_each_surface(output, view->surface, ox, oy, + render_surface_iterator, &data); +} + +static void render_popup_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *data) { + // Render this popup's surface + render_surface_iterator(output, surface, box, rotation, data); + + // Render this popup's child toplevels + output_surface_for_each_surface(output, surface, box->x, box->y, + render_surface_iterator, data); +} + +static void render_view_popups(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct render_data data = { + .damage = damage, + .alpha = alpha, + }; + output_view_for_each_popup(output, view, render_popup_iterator, &data); +} + +static void render_saved_view(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct wlr_output *wlr_output = output->wlr_output; + + if (!view->saved_buffer || !view->saved_buffer->texture) { + return; + } + struct wlr_box box = { + .x = view->container->surface_x - output->wlr_output->lx - + view->saved_geometry.x, + .y = view->container->surface_y - output->wlr_output->ly - + view->saved_geometry.y, + .width = view->saved_buffer_width, + .height = view->saved_buffer_height, + }; + + struct wlr_box output_box = { + .width = output->width, + .height = output->height, + }; + + struct wlr_box intersection; + bool intersects = wlr_box_intersection(&intersection, &output_box, &box); + if (!intersects) { + return; + } + + scale_box(&box, wlr_output->scale); + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + wlr_output->transform_matrix); + + render_texture(wlr_output, damage, view->saved_buffer->texture, + &box, matrix, alpha); +} + +/** + * Render a view's surface and left/bottom/right borders. + */ +static void render_view(struct sway_output *output, pixman_region32_t *damage, + struct sway_container *con, struct border_colors *colors) { + struct sway_view *view = con->view; + if (view->saved_buffer) { + render_saved_view(view, output, damage, view->container->alpha); + } else if (view->surface) { + render_view_toplevels(view, output, damage, view->container->alpha); + } + + if (con->current.border == B_NONE || con->current.border == B_CSD) { + return; + } + + struct wlr_box box; + float output_scale = output->wlr_output->scale; + float color[4]; + struct sway_container_state *state = &con->current; + + if (state->border_left) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = state->x; + box.y = state->content_y; + box.width = state->border_thickness; + box.height = state->content_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + list_t *siblings = container_get_current_siblings(con); + enum sway_container_layout layout = + container_current_parent_layout(con); + + if (state->border_right) { + if (siblings->length == 1 && layout == L_HORIZ) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = state->content_x + state->content_width; + box.y = state->content_y; + box.width = state->border_thickness; + box.height = state->content_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (state->border_bottom) { + if (siblings->length == 1 && layout == L_VERT) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = state->x; + box.y = state->content_y + state->content_height; + box.width = state->width; + box.height = state->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } +} + +/** + * Render a titlebar. + * + * Care must be taken not to render over the same pixel multiple times, + * otherwise the colors will be incorrect when using opacity. + * + * The height is: 1px border, 3px padding, font height, 3px padding, 1px border + * The left side for L_TABBED is: 1px border, 2px padding, title + * The left side for other layouts is: 3px padding, title + */ +static void render_titlebar(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + int x, int y, int width, + struct border_colors *colors, struct wlr_texture *title_texture, + struct wlr_texture *marks_texture) { + struct wlr_box box; + float color[4]; + struct sway_container_state *state = &con->current; + float output_scale = output->wlr_output->scale; + enum sway_container_layout layout = container_current_parent_layout(con); + 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; + int titlebar_border_thickness = config->titlebar_border_thickness; + int titlebar_h_padding = config->titlebar_h_padding; + int titlebar_v_padding = config->titlebar_v_padding; + enum alignment title_align = config->title_align; + + // Single pixel bar above title + memcpy(&color, colors->border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = x; + box.y = y; + box.width = width; + box.height = titlebar_border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel bar below title + size_t left_offset = 0, right_offset = 0; + bool connects_sides = false; + if (layout == L_HORIZ || layout == L_VERT || + (layout == L_STACKED && is_last_child)) { + if (con->view) { + left_offset = state->border_left * state->border_thickness; + right_offset = state->border_right * state->border_thickness; + connects_sides = true; + } + } + box.x = x + left_offset; + box.y = y + container_titlebar_height() - titlebar_border_thickness; + 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); + + if (layout == L_TABBED) { + // Single pixel left edge + box.x = x; + box.y = y + titlebar_border_thickness; + box.width = titlebar_border_thickness; + box.height = + container_titlebar_height() - titlebar_border_thickness * 2; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel right edge + box.x = x + width - titlebar_border_thickness; + box.y = y + titlebar_border_thickness; + box.width = titlebar_border_thickness; + box.height = + container_titlebar_height() - titlebar_border_thickness * 2; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + } + + int inner_x = x - output_x + titlebar_h_padding; + int bg_y = y + titlebar_border_thickness; + size_t inner_width = width - titlebar_h_padding * 2; + + // output-buffer local + int ob_inner_x = round(inner_x * output_scale); + int ob_inner_width = scale_length(inner_width, inner_x, output_scale); + int ob_bg_height = scale_length( + (titlebar_v_padding - titlebar_border_thickness) * 2 + + config->font_height, bg_y, output_scale); + + // Marks + int ob_marks_x = 0; // output-buffer-local + int ob_marks_width = 0; // output-buffer-local + if (config->show_marks && marks_texture) { + struct wlr_box texture_box; + wlr_texture_get_size(marks_texture, + &texture_box.width, &texture_box.height); + ob_marks_width = texture_box.width; + + // The marks texture might be shorter than the config->font_height, in + // which case we need to pad it as evenly as possible above and below. + int ob_padding_total = ob_bg_height - texture_box.height; + int ob_padding_above = floor(ob_padding_total / 2.0); + int ob_padding_below = ceil(ob_padding_total / 2.0); + + // Render texture. If the title is on the right, the marks will be on + // the left. Otherwise, they will be on the right. + if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { + texture_box.x = ob_inner_x; + } else { + texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; + } + ob_marks_x = texture_box.x; + + texture_box.y = round((bg_y - output_y) * output_scale) + + ob_padding_above; + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + if (ob_inner_width < texture_box.width) { + texture_box.width = ob_inner_width; + } + render_texture(output->wlr_output, output_damage, marks_texture, + &texture_box, matrix, con->alpha); + + // Padding above + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x + round(output_x * output_scale); + 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); + + // Padding below + box.y += ob_padding_above + texture_box.height; + box.height = ob_padding_below; + render_rect(output->wlr_output, output_damage, &box, color); + } + + // Title text + int ob_title_x = 0; // output-buffer-local + int ob_title_width = 0; // output-buffer-local + if (title_texture) { + struct wlr_box texture_box; + wlr_texture_get_size(title_texture, + &texture_box.width, &texture_box.height); + ob_title_width = texture_box.width; + + // The title texture might be shorter than the config->font_height, + // in which case we need to pad it above and below. + int ob_padding_above = round((config->font_baseline - + con->title_baseline + titlebar_v_padding - + titlebar_border_thickness) * output_scale); + int ob_padding_below = ob_bg_height - ob_padding_above - + texture_box.height; + + // Render texture + if (texture_box.width > ob_inner_width - ob_marks_width) { + texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) + ? ob_marks_x + ob_marks_width : ob_inner_x; + } else if (title_align == ALIGN_LEFT) { + texture_box.x = ob_inner_x; + } else if (title_align == ALIGN_CENTER) { + // If there are marks visible, center between the edge and marks. + // Otherwise, center in the inner area. + if (ob_marks_width) { + texture_box.x = (ob_inner_x + ob_marks_x) / 2 + - texture_box.width / 2; + } else { + texture_box.x = ob_inner_x + ob_inner_width / 2 + - texture_box.width / 2; + } + } else { + texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; + } + ob_title_x = texture_box.x; + + texture_box.y = + round((bg_y - output_y) * output_scale) + ob_padding_above; + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + if (ob_inner_width - ob_marks_width < texture_box.width) { + texture_box.width = ob_inner_width - ob_marks_width; + } + + render_texture(output->wlr_output, output_damage, title_texture, + &texture_box, matrix, con->alpha); + + // Padding above + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x + round(output_x * output_scale); + 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); + + // Padding below + box.y += ob_padding_above + texture_box.height; + box.height = ob_padding_below; + render_rect(output->wlr_output, output_damage, &box, color); + } + + // Determine the left + right extends of the textures (output-buffer local) + int ob_left_x, ob_left_width, ob_right_x, ob_right_width; + if (ob_title_width == 0 && ob_marks_width == 0) { + ob_left_x = ob_inner_x; + ob_left_width = 0; + ob_right_x = ob_inner_x; + ob_right_width = 0; + } else if (ob_title_x < ob_marks_x) { + ob_left_x = ob_title_x; + ob_left_width = ob_title_width; + ob_right_x = ob_marks_x; + ob_right_width = ob_marks_width; + } else { + ob_left_x = ob_marks_x; + ob_left_width = ob_marks_width; + ob_right_x = ob_title_x; + ob_right_width = ob_title_width; + } + if (ob_left_x < ob_inner_x) { + ob_left_x = ob_inner_x; + } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { + ob_right_x = ob_left_x; + ob_right_width = ob_left_width; + } + + // Filler between title and marks + box.width = ob_right_x - ob_left_x - ob_left_width; + if (box.width > 0) { + 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); + } + + // Padding on left side + left_offset = (layout == L_TABBED) * titlebar_border_thickness; + box.x = x + left_offset; + box.y = y + titlebar_border_thickness; + box.width = titlebar_h_padding - left_offset; + box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + + config->font_height; + scale_box(&box, output_scale); + int left_x = ob_left_x + round(output_x * output_scale); + if (box.x + box.width < left_x) { + box.width += left_x - box.x - box.width; + } + render_rect(output->wlr_output, output_damage, &box, color); + + // Padding on right side + right_offset = (layout == L_TABBED) * titlebar_border_thickness; + box.x = x + width - titlebar_h_padding; + box.y = y + titlebar_border_thickness; + box.width = titlebar_h_padding - right_offset; + box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + + config->font_height; + scale_box(&box, output_scale); + int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); + if (right_rx < box.x) { + box.width += box.x - right_rx; + box.x = right_rx; + } + render_rect(output->wlr_output, output_damage, &box, color); + + if (connects_sides) { + // Left pixel in line with bottom bar + box.x = x; + box.y = y + container_titlebar_height() - titlebar_border_thickness; + 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); + + // Right pixel in line with bottom bar + box.x = x + width - state->border_thickness * state->border_right; + box.y = y + container_titlebar_height() - titlebar_border_thickness; + 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 the top border line for a view using "border pixel". + */ +static void render_top_border(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + struct border_colors *colors) { + struct sway_container_state *state = &con->current; + if (!state->border_top) { + return; + } + struct wlr_box box; + float color[4]; + float output_scale = output->wlr_output->scale; + + // Child border - top edge + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = state->x; + box.y = state->y; + box.width = state->width; + box.height = state->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); +} + +struct parent_data { + enum sway_container_layout layout; + struct wlr_box box; + list_t *children; + bool focused; + struct sway_container *active_child; +}; + +static void render_container(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, bool parent_focused); + +/** + * Render a container's children using a L_HORIZ or L_VERT layout. + * + * Wrap child views in borders and leave child containers borderless because + * they'll apply their own borders to their children. + */ +static void render_containers_linear(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + + if (child->view) { + struct sway_view *view = child->view; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + struct sway_container_state *state = &child->current; + + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = child->marks_urgent; + } else if (state->focused || parent->focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = child->marks_focused; + } else if (child == parent->active_child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = child->marks_focused_inactive; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = child->marks_unfocused; + } + + if (state->border == B_NORMAL) { + render_titlebar(output, damage, child, state->x, + state->y, state->width, colors, + title_texture, marks_texture); + } else if (state->border == B_PIXEL) { + render_top_border(output, damage, child, colors); + } + render_view(output, damage, child, colors); + } else { + render_container(output, damage, child, + parent->focused || child->current.focused); + } + } +} + +/** + * Render a container's children using the L_TABBED layout. + */ +static void render_containers_tabbed(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + if (!parent->children->length) { + return; + } + struct sway_container *current = parent->active_child; + struct border_colors *current_colors = &config->border_colors.unfocused; + int tab_width = parent->box.width / parent->children->length; + + // Render tabs + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + struct sway_view *view = child->view; + struct sway_container_state *cstate = &child->current; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + bool urgent = view ? + view_is_urgent(view) : container_has_urgent_child(child); + + if (urgent) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = child->marks_urgent; + } else if (cstate->focused || parent->focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = child->marks_focused; + } else if (child == parent->active_child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = child->marks_focused_inactive; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = child->marks_unfocused; + } + + int x = cstate->x + tab_width * i; + + // Make last tab use the remaining width of the parent + if (i == parent->children->length - 1) { + tab_width = parent->box.width - tab_width * i; + } + + render_titlebar(output, damage, child, x, parent->box.y, tab_width, + colors, title_texture, marks_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current->view) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent->focused || current->current.focused); + } +} + +/** + * Render a container's children using the L_STACKED layout. + */ +static void render_containers_stacked(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + if (!parent->children->length) { + return; + } + struct sway_container *current = parent->active_child; + struct border_colors *current_colors = &config->border_colors.unfocused; + size_t titlebar_height = container_titlebar_height(); + + // Render titles + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + struct sway_view *view = child->view; + struct sway_container_state *cstate = &child->current; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + bool urgent = view ? + view_is_urgent(view) : container_has_urgent_child(child); + + if (urgent) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = child->marks_urgent; + } else if (cstate->focused || parent->focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = child->marks_focused; + } else if (child == parent->active_child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = child->marks_focused_inactive; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = child->marks_unfocused; + } + + int y = parent->box.y + titlebar_height * i; + render_titlebar(output, damage, child, parent->box.x, y, + parent->box.width, colors, title_texture, marks_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current->view) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent->focused || current->current.focused); + } +} + +static void render_containers(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + switch (parent->layout) { + case L_NONE: + case L_HORIZ: + case L_VERT: + render_containers_linear(output, damage, parent); + break; + case L_STACKED: + render_containers_stacked(output, damage, parent); + break; + case L_TABBED: + render_containers_tabbed(output, damage, parent); + break; + } +} + +static void render_container(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, bool focused) { + struct parent_data data = { + .layout = con->current.layout, + .box = { + .x = con->current.x, + .y = con->current.y, + .width = con->current.width, + .height = con->current.height, + }, + .children = con->current.children, + .focused = focused, + .active_child = con->current.focused_inactive_child, + }; + render_containers(output, damage, &data); +} + +static void render_workspace(struct sway_output *output, + pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { + struct parent_data data = { + .layout = ws->current.layout, + .box = { + .x = ws->current.x, + .y = ws->current.y, + .width = ws->current.width, + .height = ws->current.height, + }, + .children = ws->current.tiling, + .focused = focused, + .active_child = ws->current.focused_inactive_child, + }; + render_containers(output, damage, &data); +} + +static void render_floating_container(struct sway_output *soutput, + pixman_region32_t *damage, struct sway_container *con) { + if (con->view) { + struct sway_view *view = con->view; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = con->title_urgent; + marks_texture = con->marks_urgent; + } else if (con->current.focused) { + colors = &config->border_colors.focused; + title_texture = con->title_focused; + marks_texture = con->marks_focused; + } else { + colors = &config->border_colors.unfocused; + title_texture = con->title_unfocused; + marks_texture = con->marks_unfocused; + } + + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.x, + con->current.y, con->current.width, colors, + title_texture, marks_texture); + } else if (con->current.border == B_PIXEL) { + render_top_border(soutput, damage, con, colors); + } + render_view(soutput, damage, con, colors); + } else { + render_container(soutput, damage, con, con->current.focused); + } +} + +static void render_floating(struct sway_output *soutput, + pixman_region32_t *damage) { + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + for (int j = 0; j < output->current.workspaces->length; ++j) { + struct sway_workspace *ws = output->current.workspaces->items[j]; + if (!workspace_is_visible(ws)) { + continue; + } + for (int k = 0; k < ws->current.floating->length; ++k) { + struct sway_container *floater = ws->current.floating->items[k]; + render_floating_container(soutput, damage, floater); + } + } + } +} + +static void render_seatops(struct sway_output *output, + pixman_region32_t *damage) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seatop_render(seat, output, damage); + } +} + +void output_render(struct sway_output *output, struct timespec *when, + pixman_region32_t *damage) { + struct wlr_output *wlr_output = output->wlr_output; + + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + if (!sway_assert(renderer != NULL, + "expected the output backend to have a renderer")) { + return; + } + + struct sway_workspace *workspace = output->current.active_workspace; + if (workspace == NULL) { + return; + } + + wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); + + if (!pixman_region32_not_empty(damage)) { + // Output isn't damaged but needs buffer swap + goto renderer_end; + } + + if (debug.damage == DAMAGE_HIGHLIGHT) { + wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); + } else if (debug.damage == DAMAGE_RERENDER) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(damage, damage, 0, 0, width, height); + } + + if (output_has_opaque_overlay_layer_surface(output)) { + goto render_overlay; + } + + 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; + } + + if (fullscreen_con) { + float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_renderer_clear(renderer, clear_color); + } + + if (fullscreen_con->view) { + if (fullscreen_con->view->saved_buffer) { + render_saved_view(fullscreen_con->view, output, damage, 1.0f); + } else if (fullscreen_con->view->surface) { + render_view_toplevels(fullscreen_con->view, + output, damage, 1.0f); + } + } else { + render_container(output, damage, fullscreen_con, + fullscreen_con->current.focused); + } + + for (int i = 0; i < workspace->current.floating->length; ++i) { + struct sway_container *floater = + workspace->current.floating->items[i]; + if (container_is_transient_for(floater, fullscreen_con)) { + render_floating_container(output, damage, floater); + } + } +#if HAVE_XWAYLAND + render_unmanaged(output, damage, &root->xwayland_unmanaged); +#endif + } else { + float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_renderer_clear(renderer, clear_color); + } + + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + + render_workspace(output, damage, workspace, workspace->current.focused); + render_floating(output, damage); +#if HAVE_XWAYLAND + render_unmanaged(output, damage, &root->xwayland_unmanaged); +#endif + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + } + + render_seatops(output, damage); + + struct sway_seat *seat = input_manager_current_seat(); + struct sway_container *focus = seat_get_focused_container(seat); + if (focus && focus->view) { + render_view_popups(focus->view, output, damage, focus->alpha); + } + +render_overlay: + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_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); + + 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); + } + + enum wl_output_transform transform = + wlr_output_transform_invert(wlr_output->transform); + wlr_region_transform(damage, damage, transform, width, height); + + if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { + return; + } + output->last_frame = *when; +} diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c deleted file mode 100644 index d8d276451..000000000 --- a/sway/desktop/tearing.c +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include -#include "sway/server.h" -#include "sway/tree/view.h" -#include "sway/output.h" -#include "log.h" - -struct sway_tearing_controller { - struct wlr_tearing_control_v1 *tearing_control; - struct wl_listener set_hint; - struct wl_listener destroy; - - struct wl_list link; // sway_server::tearing_controllers -}; - -static void handle_tearing_controller_set_hint(struct wl_listener *listener, - void *data) { - struct sway_tearing_controller *controller = - wl_container_of(listener, controller, set_hint); - - struct sway_view *view = view_from_wlr_surface( - controller->tearing_control->surface); - if (view) { - view->tearing_hint = controller->tearing_control->current; - } -} - -static void handle_tearing_controller_destroy(struct wl_listener *listener, - void *data) { - struct sway_tearing_controller *controller = - wl_container_of(listener, controller, destroy); - wl_list_remove(&controller->set_hint.link); - wl_list_remove(&controller->destroy.link); - wl_list_remove(&controller->link); - free(controller); -} - -void handle_new_tearing_hint(struct wl_listener *listener, - void *data) { - struct sway_server *server = - wl_container_of(listener, server, tearing_control_new_object); - struct wlr_tearing_control_v1 *tearing_control = data; - - enum wp_tearing_control_v1_presentation_hint hint = - wlr_tearing_control_manager_v1_surface_hint_from_surface( - server->tearing_control_v1, tearing_control->surface); - sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p", - hint, tearing_control->surface); - - struct sway_tearing_controller *controller = - calloc(1, sizeof(struct sway_tearing_controller)); - if (!controller) { - return; - } - - controller->tearing_control = tearing_control; - controller->set_hint.notify = handle_tearing_controller_set_hint; - wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint); - controller->destroy.notify = handle_tearing_controller_destroy; - wl_signal_add(&tearing_control->events.destroy, &controller->destroy); - wl_list_init(&controller->link); - - wl_list_insert(&server->tearing_controllers, &controller->link); -} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d4e853dee..4098ed22c 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -1,16 +1,17 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "sway/config.h" -#include "sway/scene_descriptor.h" +#include "sway/debug.h" +#include "sway/desktop.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/output.h" -#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/node.h" #include "sway/tree/view.h" @@ -35,8 +36,6 @@ struct sway_transaction_instruction { struct sway_container_state container_state; }; uint32_t serial; - bool server_request; - bool waiting; }; static struct sway_transaction *transaction_create(void) { @@ -88,11 +87,7 @@ static void transaction_destroy(struct sway_transaction *transaction) { static void copy_output_state(struct sway_output *output, struct sway_transaction_instruction *instruction) { struct sway_output_state *state = &instruction->output_state; - if (state->workspaces) { - state->workspaces->length = 0; - } else { - state->workspaces = create_list(); - } + state->workspaces = create_list(); list_cat(state->workspaces, output->workspaces); state->active_workspace = output_get_active_workspace(output); @@ -110,16 +105,8 @@ static void copy_workspace_state(struct sway_workspace *ws, state->layout = ws->layout; state->output = ws->output; - if (state->floating) { - state->floating->length = 0; - } else { - state->floating = create_list(); - } - if (state->tiling) { - state->tiling->length = 0; - } else { - state->tiling = create_list(); - } + state->floating = create_list(); + state->tiling = create_list(); list_cat(state->floating, ws->floating); list_cat(state->tiling, ws->tiling); @@ -129,8 +116,8 @@ static void copy_workspace_state(struct sway_workspace *ws, // Set focused_inactive_child to the direct tiling child struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); if (focus) { - while (focus->pending.parent) { - focus = focus->pending.parent; + while (focus->parent) { + focus = focus->parent; } } state->focused_inactive_child = focus; @@ -140,19 +127,28 @@ static void copy_container_state(struct sway_container *container, struct sway_transaction_instruction *instruction) { struct sway_container_state *state = &instruction->container_state; - if (state->children) { - list_free(state->children); - } - - memcpy(state, &container->pending, sizeof(struct sway_container_state)); + state->layout = container->layout; + state->x = container->x; + state->y = container->y; + state->width = container->width; + state->height = container->height; + state->fullscreen_mode = container->fullscreen_mode; + state->parent = container->parent; + state->workspace = container->workspace; + state->border = container->border; + state->border_thickness = container->border_thickness; + state->border_top = container->border_top; + state->border_left = container->border_left; + state->border_right = container->border_right; + state->border_bottom = container->border_bottom; + state->content_x = container->content_x; + state->content_y = container->content_y; + state->content_width = container->content_width; + state->content_height = container->content_height; if (!container->view) { - // We store a copy of the child list to avoid having it mutated after - // we copy the state. state->children = create_list(); - list_cat(state->children, container->pending.children); - } else { - state->children = NULL; + list_cat(state->children, container->children); } struct sway_seat *seat = input_manager_current_seat(); @@ -166,36 +162,14 @@ static void copy_container_state(struct sway_container *container, } static void transaction_add_node(struct sway_transaction *transaction, - struct sway_node *node, bool server_request) { - struct sway_transaction_instruction *instruction = NULL; - - // Check if we have an instruction for this node already, in which case we - // update that instead of creating a new one. - if (node->ntxnrefs > 0) { - for (int idx = 0; idx < transaction->instructions->length; idx++) { - struct sway_transaction_instruction *other = - transaction->instructions->items[idx]; - if (other->node == node) { - instruction = other; - break; - } - } - } - - if (!instruction) { - instruction = calloc(1, sizeof(struct sway_transaction_instruction)); - if (!sway_assert(instruction, "Unable to allocate instruction")) { - return; - } - instruction->transaction = transaction; - instruction->node = node; - instruction->server_request = server_request; - - list_add(transaction->instructions, instruction); - node->ntxnrefs++; - } else if (server_request) { - instruction->server_request = true; + struct sway_node *node) { + struct sway_transaction_instruction *instruction = + calloc(1, sizeof(struct sway_transaction_instruction)); + if (!sway_assert(instruction, "Unable to allocate instruction")) { + return; } + instruction->transaction = transaction; + instruction->node = node; switch (node->type) { case N_ROOT: @@ -210,24 +184,43 @@ static void transaction_add_node(struct sway_transaction *transaction, copy_container_state(node->sway_container, instruction); break; } + + list_add(transaction->instructions, instruction); + node->ntxnrefs++; } static void apply_output_state(struct sway_output *output, struct sway_output_state *state) { + output_damage_whole(output); list_free(output->current.workspaces); memcpy(&output->current, state, sizeof(struct sway_output_state)); + output_damage_whole(output); } static void apply_workspace_state(struct sway_workspace *ws, struct sway_workspace_state *state) { + output_damage_whole(ws->current.output); list_free(ws->current.floating); list_free(ws->current.tiling); memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); + output_damage_whole(ws->current.output); } static void apply_container_state(struct sway_container *container, struct sway_container_state *state) { struct sway_view *view = container->view; + // Damage the old location + desktop_damage_whole_container(container); + if (view && view->saved_buffer) { + struct wlr_box box = { + .x = container->current.content_x - view->saved_geometry.x, + .y = container->current.content_y - view->saved_geometry.y, + .width = view->saved_buffer_width, + .height = view->saved_buffer_height, + }; + desktop_damage_box(&box); + } + // There are separate children lists for each instruction state, the // container's current state and the container's pending state // (ie. con->children). The list itself needs to be freed here. @@ -237,479 +230,50 @@ static void apply_container_state(struct sway_container *container, memcpy(&container->current, state, sizeof(struct sway_container_state)); - if (view) { - if (view->saved_surface_tree) { - if (!container->node.destroying || container->node.ntxnrefs == 1) { - view_remove_saved_buffer(view); - } - } - - // If the view hasn't responded to the configure, center it within - // the container. This is important for fullscreen views which - // refuse to resize to the size of the output. - if (view->surface) { - view_center_and_clip_surface(view); + if (view && view->saved_buffer) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { + view_remove_saved_buffer(view); } } -} -static void arrange_title_bar(struct sway_container *con, - int x, int y, int width, int height) { - container_update(con); - - bool has_title_bar = height > 0; - wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); - if (!has_title_bar) { - return; + // Damage the new location + desktop_damage_whole_container(container); + if (view && view->surface) { + struct wlr_surface *surface = view->surface; + struct wlr_box box = { + .x = container->current.content_x - view->geometry.x, + .y = container->current.content_y - view->geometry.y, + .width = surface->current.width, + .height = surface->current.height, + }; + desktop_damage_box(&box); } - wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); - - con->title_width = width; - container_arrange_title_bar(con); -} - -static void disable_container(struct sway_container *con) { - if (con->view) { - wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); - } else { - for (int i = 0; i < con->current.children->length; i++) { - struct sway_container *child = con->current.children->items[i]; - - wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); - - disable_container(child); - } - } -} - -static void arrange_container(struct sway_container *con, - int width, int height, bool title_bar, int gaps); - -static void arrange_children(enum sway_container_layout layout, list_t *children, - struct sway_container *active, struct wlr_scene_tree *content, - int width, int height, int gaps) { - int title_bar_height = container_titlebar_height(); - - if (layout == L_TABBED) { - struct sway_container *first = children->length == 1 ? - ((struct sway_container *)children->items[0]) : NULL; - if (config->hide_lone_tab && first && first->view && - first->current.border != B_NORMAL) { - title_bar_height = 0; - } - - double w = (double) width / children->length; - int title_offset = 0; - for (int i = 0; i < children->length; i++) { - struct sway_container *child = children->items[i]; - bool activated = child == active; - int next_title_offset = round(w * i + w); - - arrange_title_bar(child, title_offset, -title_bar_height, - next_title_offset - title_offset, title_bar_height); - wlr_scene_node_set_enabled(&child->border.tree->node, activated); - wlr_scene_node_set_enabled(&child->scene_tree->node, true); - wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); - wlr_scene_node_reparent(&child->scene_tree->node, content); - - int net_height = height - title_bar_height; - if (activated && width > 0 && net_height > 0) { - arrange_container(child, width, net_height, title_bar_height == 0, 0); - } else { - disable_container(child); - } - - title_offset = next_title_offset; - } - } else if (layout == L_STACKED) { - struct sway_container *first = children->length == 1 ? - ((struct sway_container *)children->items[0]) : NULL; - if (config->hide_lone_tab && first && first->view && - first->current.border != B_NORMAL) { - title_bar_height = 0; - } - - int title_height = title_bar_height * children->length; - - int y = 0; - for (int i = 0; i < children->length; i++) { - struct sway_container *child = children->items[i]; - bool activated = child == active; - - arrange_title_bar(child, 0, y - title_height, width, title_bar_height); - wlr_scene_node_set_enabled(&child->border.tree->node, activated); - wlr_scene_node_set_enabled(&child->scene_tree->node, true); - wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); - wlr_scene_node_reparent(&child->scene_tree->node, content); - - int net_height = height - title_height; - if (activated && width > 0 && net_height > 0) { - arrange_container(child, width, net_height, title_bar_height == 0, 0); - } else { - disable_container(child); - } - - y += title_bar_height; - } - } else if (layout == L_VERT) { - int off = 0; - for (int i = 0; i < children->length; i++) { - struct sway_container *child = children->items[i]; - int cheight = child->current.height; - - wlr_scene_node_set_enabled(&child->border.tree->node, true); - wlr_scene_node_set_position(&child->scene_tree->node, 0, off); - wlr_scene_node_reparent(&child->scene_tree->node, content); - if (width > 0 && cheight > 0) { - arrange_container(child, width, cheight, true, gaps); - off += cheight + gaps; - } else { - disable_container(child); - } - } - } else if (layout == L_HORIZ) { - int off = 0; - for (int i = 0; i < children->length; i++) { - struct sway_container *child = children->items[i]; - int cwidth = child->current.width; - - wlr_scene_node_set_enabled(&child->border.tree->node, true); - wlr_scene_node_set_position(&child->scene_tree->node, off, 0); - wlr_scene_node_reparent(&child->scene_tree->node, content); - if (cwidth > 0 && height > 0) { - arrange_container(child, cwidth, height, true, gaps); - off += cwidth + gaps; - } else { - disable_container(child); - } - } - } else { - sway_assert(false, "unreachable"); - } -} - -static void arrange_container(struct sway_container *con, - int width, int height, bool title_bar, int gaps) { - // this container might have previously been in the scratchpad, - // make sure it's enabled for viewing - wlr_scene_node_set_enabled(&con->scene_tree->node, true); - - if (con->view) { - int border_top = container_titlebar_height(); - int border_width = con->current.border_thickness; - - if (title_bar && con->current.border != B_NORMAL) { - wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); - wlr_scene_node_set_enabled(&con->border.top->node, true); + // If the view hasn't responded to the configure, center it within + // the container. This is important for fullscreen views which + // refuse to resize to the size of the output. + if (view && view->surface) { + if (view->surface->current.width < container->width) { + container->surface_x = container->content_x + + (container->content_width - view->surface->current.width) / 2; } else { - wlr_scene_node_set_enabled(&con->border.top->node, false); + container->surface_x = container->content_x; } - - if (con->current.border == B_NORMAL) { - if (title_bar) { - arrange_title_bar(con, 0, 0, width, border_top); - } else { - border_top = 0; - // should be handled by the parent container - } - } else if (con->current.border == B_PIXEL) { - container_update(con); - border_top = title_bar && con->current.border_top ? border_width : 0; - } else if (con->current.border == B_NONE) { - container_update(con); - border_top = 0; - border_width = 0; - } else if (con->current.border == B_CSD) { - border_top = 0; - border_width = 0; + if (view->surface->current.height < container->height) { + container->surface_y = container->content_y + + (container->content_height - view->surface->current.height) / 2; } else { - sway_assert(false, "unreachable"); + container->surface_y = container->content_y; } - - int border_bottom = con->current.border_bottom ? border_width : 0; - int border_left = con->current.border_left ? border_width : 0; - int border_right = con->current.border_right ? border_width : 0; - int vert_border_height = MAX(0, height - border_top - border_bottom); - - wlr_scene_rect_set_size(con->border.top, width, border_top); - wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); - wlr_scene_rect_set_size(con->border.left, - border_left, vert_border_height); - wlr_scene_rect_set_size(con->border.right, - border_right, vert_border_height); - - wlr_scene_node_set_position(&con->border.top->node, 0, 0); - wlr_scene_node_set_position(&con->border.bottom->node, - 0, height - border_bottom); - wlr_scene_node_set_position(&con->border.left->node, - 0, border_top); - wlr_scene_node_set_position(&con->border.right->node, - width - border_right, border_top); - - // make sure to reparent, it's possible that the client just came out of - // fullscreen mode where the parent of the surface is not the container - wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); - wlr_scene_node_set_position(&con->view->scene_tree->node, - border_left, border_top); - - // the output handler for the view wants to detect events for the entire - // container so give it negative coordinates to move it back over the - // decorations - wlr_scene_node_set_position(&con->view->output_handler->node, - -border_left, -border_top); - wlr_scene_buffer_set_dest_size(con->view->output_handler, width, height); - } else { - // make sure to disable the title bar if the parent is not managing it - if (title_bar) { - wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); - } - - arrange_children(con->current.layout, con->current.children, - con->current.focused_inactive_child, con->content_tree, - width, height, gaps); - } -} - -static int container_get_gaps(struct sway_container *con) { - struct sway_workspace *ws = con->current.workspace; - struct sway_container *temp = con; - while (temp) { - enum sway_container_layout layout; - if (temp->current.parent) { - layout = temp->current.parent->current.layout; - } else { - layout = ws->current.layout; - } - if (layout == L_TABBED || layout == L_STACKED) { - return 0; - } - temp = temp->pending.parent; - } - return ws->gaps_inner; -} - -static void arrange_fullscreen(struct wlr_scene_tree *tree, - struct sway_container *fs, struct sway_workspace *ws, - int width, int height) { - struct wlr_scene_node *fs_node; - if (fs->view) { - fs_node = &fs->view->scene_tree->node; - - // if we only care about the view, disable any decorations - wlr_scene_node_set_enabled(&fs->scene_tree->node, false); - - // reconfigure the output handler (for foreign toplevel) to cover the - // view without container decorations - wlr_scene_node_set_position(&fs->view->output_handler->node, 0, 0); - wlr_scene_buffer_set_dest_size(fs->view->output_handler, width, height); - } else { - fs_node = &fs->scene_tree->node; - arrange_container(fs, width, height, true, container_get_gaps(fs)); + container->surface_width = view->surface->current.width; + container->surface_height = view->surface->current.height; } - wlr_scene_node_reparent(fs_node, tree); - wlr_scene_node_lower_to_bottom(fs_node); - wlr_scene_node_set_position(fs_node, 0, 0); -} - -static void arrange_workspace_floating(struct sway_workspace *ws) { - for (int i = 0; i < ws->current.floating->length; i++) { - struct sway_container *floater = ws->current.floating->items[i]; - struct wlr_scene_tree *layer = root->layers.floating; - - if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { - continue; - } - - if (root->fullscreen_global) { - if (container_is_transient_for(floater, root->fullscreen_global)) { - layer = root->layers.fullscreen_global; - } - } else { - for (int i = 0; i < root->outputs->length; i++) { - struct sway_output *output = root->outputs->items[i]; - struct sway_workspace *active = output->current.active_workspace; - - if (active && active->fullscreen && - container_is_transient_for(floater, active->fullscreen)) { - layer = root->layers.fullscreen; - } - } - } - - wlr_scene_node_reparent(&floater->scene_tree->node, layer); - wlr_scene_node_set_position(&floater->scene_tree->node, - floater->current.x, floater->current.y); - wlr_scene_node_set_enabled(&floater->scene_tree->node, true); - wlr_scene_node_set_enabled(&floater->border.tree->node, true); - - arrange_container(floater, floater->current.width, floater->current.height, - true, ws->gaps_inner); + if (!container->node.destroying) { + container_discover_outputs(container); } } -static void arrange_workspace_tiling(struct sway_workspace *ws, - int width, int height) { - arrange_children(ws->current.layout, ws->current.tiling, - ws->current.focused_inactive_child, ws->layers.tiling, - width, height, ws->gaps_inner); -} - -static void disable_workspace(struct sway_workspace *ws) { - // if any containers were just moved to a disabled workspace it will - // have the parent of the old workspace. Move the workspace so that it won't - // be shown. - for (int i = 0; i < ws->current.tiling->length; i++) { - struct sway_container *child = ws->current.tiling->items[i]; - - wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); - disable_container(child); - } - - for (int i = 0; i < ws->current.floating->length; i++) { - struct sway_container *floater = ws->current.floating->items[i]; - wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); - disable_container(floater); - wlr_scene_node_set_enabled(&floater->scene_tree->node, false); - } -} - -static void arrange_output(struct sway_output *output, int width, int height) { - for (int i = 0; i < output->current.workspaces->length; i++) { - struct sway_workspace *child = output->current.workspaces->items[i]; - - bool activated = output->current.active_workspace == child && output->wlr_output->enabled; - - wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); - wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); - - for (int i = 0; i < child->current.floating->length; i++) { - struct sway_container *floater = child->current.floating->items[i]; - wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); - wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); - } - - if (activated) { - struct sway_container *fs = child->current.fullscreen; - wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); - wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); - - wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); - wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); - wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); - - if (fs) { - disable_workspace(child); - - wlr_scene_rect_set_size(output->fullscreen_background, width, height); - - arrange_workspace_floating(child); - arrange_fullscreen(child->layers.fullscreen, fs, child, - width, height); - } else { - struct wlr_box *area = &output->usable_area; - struct side_gaps *gaps = &child->current_gaps; - - wlr_scene_node_set_position(&child->layers.tiling->node, - gaps->left + area->x, gaps->top + area->y); - - arrange_workspace_tiling(child, - area->width - gaps->left - gaps->right, - area->height - gaps->top - gaps->bottom); - arrange_workspace_floating(child); - } - } else { - wlr_scene_node_set_enabled(&child->layers.tiling->node, false); - wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); - - disable_workspace(child); - } - } -} - -void arrange_popups(struct wlr_scene_tree *popups) { - struct wlr_scene_node *node; - wl_list_for_each(node, &popups->children, link) { - struct sway_popup_desc *popup = scene_descriptor_try_get(node, - SWAY_SCENE_DESC_POPUP); - - if (popup) { - int lx, ly; - wlr_scene_node_coords(popup->relative, &lx, &ly); - wlr_scene_node_set_position(node, lx, ly); - } - } -} - -static void arrange_root(struct sway_root *root) { - struct sway_container *fs = root->fullscreen_global; - - wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); - wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); - wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); - wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); - wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); - wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); - - // hide all contents in the scratchpad - for (int i = 0; i < root->scratchpad->length; i++) { - struct sway_container *con = root->scratchpad->items[i]; - - disable_container(con); - wlr_scene_node_set_enabled(&con->scene_tree->node, false); - } - - if (fs) { - for (int i = 0; i < root->outputs->length; i++) { - struct sway_output *output = root->outputs->items[i]; - struct sway_workspace *ws = output->current.active_workspace; - - wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); - - // disable all workspaces to get to a known state - for (int j = 0; j < output->current.workspaces->length; j++) { - struct sway_workspace *workspace = output->current.workspaces->items[j]; - disable_workspace(workspace); - } - - // arrange the active workspace - if (ws) { - arrange_workspace_floating(ws); - } - } - - arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, - root->width, root->height); - } else { - for (int i = 0; i < root->outputs->length; i++) { - struct sway_output *output = root->outputs->items[i]; - - wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); - - wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); - wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); - wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); - wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); - wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); - wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); - wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); - - wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); - wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); - wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); - wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); - wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); - wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); - wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); - - arrange_output(output, output->width, output->height); - } - } - - arrange_popups(root->layers.popup); -} - /** * Apply a transaction to the "current" state of the tree. */ @@ -749,29 +313,63 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } + + cursor_rebase_all(); } -static void transaction_commit_pending(void); +static void transaction_commit(struct sway_transaction *transaction); -static void transaction_progress(void) { - if (!server.queued_transaction) { +// Return true if both transactions operate on the same nodes +static bool transaction_same_nodes(struct sway_transaction *a, + struct sway_transaction *b) { + if (a->instructions->length != b->instructions->length) { + return false; + } + for (int i = 0; i < a->instructions->length; ++i) { + struct sway_transaction_instruction *a_inst = a->instructions->items[i]; + struct sway_transaction_instruction *b_inst = b->instructions->items[i]; + if (a_inst->node != b_inst->node) { + return false; + } + } + return true; +} + +static void transaction_progress_queue(void) { + if (!server.transactions->length) { return; } - if (server.queued_transaction->num_waiting > 0) { + // There's only ever one committed transaction, + // and it's the first one in the queue. + struct sway_transaction *transaction = server.transactions->items[0]; + if (transaction->num_waiting) { return; } - transaction_apply(server.queued_transaction); - arrange_root(root); - cursor_rebase_all(); - transaction_destroy(server.queued_transaction); - server.queued_transaction = NULL; + transaction_apply(transaction); + transaction_destroy(transaction); + list_del(server.transactions, 0); - if (!server.pending_transaction) { - sway_idle_inhibit_v1_check_active(); + if (!server.transactions->length) { + idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); return; } - transaction_commit_pending(); + // If there's a bunch of consecutive transactions which all apply to the + // same views, skip all except the last one. + while (server.transactions->length >= 2) { + struct sway_transaction *a = server.transactions->items[0]; + struct sway_transaction *b = server.transactions->items[1]; + if (transaction_same_nodes(a, b)) { + list_del(server.transactions, 0); + transaction_destroy(a); + } else { + break; + } + } + + transaction = server.transactions->items[0]; + transaction_commit(transaction); + transaction_progress_queue(); } static int handle_timeout(void *data) { @@ -779,7 +377,7 @@ static int handle_timeout(void *data) { sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", transaction, transaction->num_waiting); transaction->num_waiting = 0; - transaction_progress(); + transaction_progress_queue(); return 0; } @@ -791,21 +389,14 @@ static bool should_configure(struct sway_node *node, if (node->destroying) { return false; } - if (!instruction->server_request) { - return false; - } struct sway_container_state *cstate = &node->sway_container->current; struct sway_container_state *istate = &instruction->container_state; -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND // Xwayland views are position-aware and need to be reconfigured // when their position changes. if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { - // Sway logical coordinates are doubles, but they get truncated to - // integers when sent to Xwayland through `xcb_configure_window`. - // X11 apps will not respond to duplicate configure requests (from their - // truncated point of view) and cause transactions to time out. - if ((int)cstate->content_x != (int)istate->content_x || - (int)cstate->content_y != (int)istate->content_y) { + if (cstate->content_x != istate->content_x || + cstate->content_y != istate->content_y) { return true; } } @@ -825,24 +416,27 @@ static void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_node *node = instruction->node; - bool hidden = node_is_view(node) && !node->destroying && - !view_is_visible(node->sway_container->view); if (should_configure(node, instruction)) { instruction->serial = view_configure(node->sway_container->view, instruction->container_state.content_x, instruction->container_state.content_y, instruction->container_state.content_width, instruction->container_state.content_height); - if (!hidden) { - instruction->waiting = true; - ++transaction->num_waiting; - } + ++transaction->num_waiting; - view_send_frame_done(node->sway_container->view); + // From here on we are rendering a saved buffer of the view, which + // 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; + wlr_surface_send_frame_done( + node->sway_container->view->surface, &when); } - if (!hidden && node_is_view(node) && - !node->sway_container->view->saved_surface_tree) { + if (node_is_view(node) && !node->sway_container->view->saved_buffer) { view_save_buffer(node->sway_container->view); + memcpy(&node->sway_container->view->saved_geometry, + &node->sway_container->view->geometry, + sizeof(struct wlr_box)); } node->instruction = instruction; } @@ -871,17 +465,11 @@ static void transaction_commit(struct sway_transaction *transaction) { transaction->num_waiting = 0; } } -} -static void transaction_commit_pending(void) { - if (server.queued_transaction) { - return; - } - struct sway_transaction *transaction = server.pending_transaction; - server.pending_transaction = NULL; - server.queued_transaction = transaction; - transaction_commit(transaction); - transaction_progress(); + // 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( @@ -902,68 +490,57 @@ static void set_instruction_ready( } // If the transaction has timed out then its num_waiting will be 0 already. - if (instruction->waiting && transaction->num_waiting > 0 && - --transaction->num_waiting == 0) { + if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); } instruction->node->instruction = NULL; - transaction_progress(); + transaction_progress_queue(); } -bool transaction_notify_view_ready_by_serial(struct sway_view *view, +void transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial) { struct sway_transaction_instruction *instruction = view->container->node.instruction; - if (instruction != NULL && instruction->serial == serial) { + if (instruction->serial == serial) { set_instruction_ready(instruction); - return true; } - return false; } -bool transaction_notify_view_ready_by_geometry(struct sway_view *view, - double x, double y, int width, int height) { +void transaction_notify_view_ready_by_size(struct sway_view *view, + int width, int height) { struct sway_transaction_instruction *instruction = view->container->node.instruction; - if (instruction != NULL && - (int)instruction->container_state.content_x == (int)x && - (int)instruction->container_state.content_y == (int)y && - instruction->container_state.content_width == width && + if (instruction->container_state.content_width == width && instruction->container_state.content_height == height) { set_instruction_ready(instruction); - return true; } - return false; } -static void _transaction_commit_dirty(bool server_request) { +void transaction_commit_dirty(void) { if (!server.dirty_nodes->length) { return; } - - if (!server.pending_transaction) { - server.pending_transaction = transaction_create(); - if (!server.pending_transaction) { - return; - } + struct sway_transaction *transaction = transaction_create(); + if (!transaction) { + return; } - for (int i = 0; i < server.dirty_nodes->length; ++i) { struct sway_node *node = server.dirty_nodes->items[i]; - transaction_add_node(server.pending_transaction, node, server_request); + transaction_add_node(transaction, node); node->dirty = false; } server.dirty_nodes->length = 0; - transaction_commit_pending(); -} + list_add(server.transactions, transaction); -void transaction_commit_dirty(void) { - _transaction_commit_dirty(true); -} - -void transaction_commit_dirty_client(void) { - _transaction_commit_dirty(false); + // There's only ever one committed transaction, + // and it's the first one in the queue. + if (server.transactions->length == 1) { + transaction_commit(transaction); + // Attempting to progress the queue here is useful + // if the transaction has nothing to wait for. + transaction_progress_queue(); + } } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 7217e1369..ce6fe41a1 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,13 +1,13 @@ +#define _POSIX_C_SOURCE 199309L #include #include #include -#include +#include #include -#include #include #include "log.h" #include "sway/decoration.h" -#include "sway/scene_descriptor.h" +#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -17,47 +17,65 @@ #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; + +static void popup_get_root_coords(struct sway_view_child *child, + int *root_sx, int *root_sy) { + struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; + struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; + + int x_offset = -child->view->geometry.x - surface->geometry.x; + int y_offset = -child->view->geometry.y - surface->geometry.y; + + wlr_xdg_popup_get_toplevel_coords(surface->popup, + x_offset + surface->popup->geometry.x, + y_offset + surface->popup->geometry.y, + root_sx, root_sy); +} + +static void popup_destroy(struct sway_view_child *child) { + if (!sway_assert(child->impl == &popup_impl, + "Expected an xdg_shell popup")) { + return; + } + struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; + wl_list_remove(&popup->new_popup.link); + wl_list_remove(&popup->destroy.link); + free(popup); +} + +static const struct sway_view_child_impl popup_impl = { + .get_root_coords = popup_get_root_coords, + .destroy = popup_destroy, +}; static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view, - struct wlr_scene_tree *parent, struct wlr_scene_tree *image_capture_parent); + struct wlr_xdg_popup *wlr_popup, struct sway_view *view); static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_popup *popup = wl_container_of(listener, popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, popup->view, popup->xdg_surface_tree, popup->image_capture_tree); + popup_create(wlr_popup, popup->child.view); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); - - wl_list_remove(&popup->new_popup.link); - wl_list_remove(&popup->destroy.link); - wl_list_remove(&popup->surface_commit.link); - wl_list_remove(&popup->reposition.link); - wlr_scene_node_destroy(&popup->scene_tree->node); - free(popup); + view_child_destroy(&popup->child); } static void popup_unconstrain(struct sway_xdg_popup *popup) { - struct sway_view *view = popup->view; - struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; + struct sway_view *view = popup->child.view; + struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; - struct sway_workspace *workspace = view->container->pending.workspace; - if (!workspace) { - // is null if in the scratchpad - return; - } - - struct sway_output *output = workspace->output; + struct sway_output *output = view->container->workspace->output; // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = output->lx - view->container->pending.content_x + view->geometry.x, - .y = output->ly - view->container->pending.content_y + view->geometry.y, + .x = output->lx - view->container->content_x, + .y = output->ly - view->container->content_y, .width = output->width, .height = output->height, }; @@ -65,78 +83,32 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); } -static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit); - if (popup->wlr_xdg_popup->base->initial_commit) { - popup_unconstrain(popup); - } -} - -static void popup_handle_reposition(struct wl_listener *listener, void *data) { - struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition); - popup_unconstrain(popup); -} - -static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, - struct sway_view *view, struct wlr_scene_tree *parent, - struct wlr_scene_tree *image_capture_parent) { +static struct sway_xdg_popup *popup_create( + struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; - struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); - if (!popup) { + struct sway_xdg_popup *popup = + calloc(1, sizeof(struct sway_xdg_popup)); + if (popup == NULL) { return NULL; } + view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); + popup->wlr_xdg_surface = xdg_surface; - popup->wlr_xdg_popup = wlr_popup; - popup->view = view; - - popup->scene_tree = wlr_scene_tree_create(parent); - if (!popup->scene_tree) { - free(popup); - return NULL; - } - - popup->xdg_surface_tree = wlr_scene_xdg_surface_create( - popup->scene_tree, xdg_surface); - if (!popup->xdg_surface_tree) { - wlr_scene_node_destroy(&popup->scene_tree->node); - free(popup); - return NULL; - } - - popup->desc.relative = &view->content_tree->node; - popup->desc.view = view; - - if (!scene_descriptor_assign(&popup->scene_tree->node, - SWAY_SCENE_DESC_POPUP, &popup->desc)) { - sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); - wlr_scene_node_destroy(&popup->scene_tree->node); - free(popup); - return NULL; - } - - popup->image_capture_tree = wlr_scene_xdg_surface_create(image_capture_parent, xdg_surface); - if (popup->image_capture_tree == NULL) { - return NULL; - } - - popup->wlr_xdg_popup = xdg_surface->popup; - struct sway_xdg_shell_view *shell_view = - wl_container_of(view, shell_view, view); - xdg_surface->data = shell_view; - - wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); - popup->surface_commit.notify = popup_handle_surface_commit; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; - wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); - popup->reposition.notify = popup_handle_reposition; - wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); + wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); + wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); + + popup_unconstrain(popup); + return popup; } + static struct sway_xdg_shell_view *xdg_shell_view_from_view( struct sway_view *view) { if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, @@ -149,7 +121,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( static void get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height) { struct wlr_xdg_toplevel_state *state = - &view->wlr_xdg_toplevel->current; + &view->wlr_xdg_surface->toplevel->current; *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; @@ -158,17 +130,14 @@ static void get_constraints(struct sway_view *view, double *min_width, static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { - struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); - if (xdg_shell_view == NULL) { + if (xdg_shell_view_from_view(view) == NULL) { return NULL; } switch (prop) { case VIEW_PROP_TITLE: - return view->wlr_xdg_toplevel->title; + return view->wlr_xdg_surface->toplevel->title; case VIEW_PROP_APP_ID: - return view->wlr_xdg_toplevel->app_id; - case VIEW_PROP_TAG: - return xdg_shell_view->tag; + return view->wlr_xdg_surface->toplevel->app_id; default: return NULL; } @@ -181,52 +150,42 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, if (xdg_shell_view == NULL) { return 0; } - return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, - width, height); + return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); } static void set_activated(struct sway_view *view, bool activated) { if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); + struct wlr_xdg_surface *surface = view->wlr_xdg_surface; + if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + wlr_xdg_toplevel_set_activated(surface, activated); + } } static void set_tiled(struct sway_view *view, bool tiled) { if (xdg_shell_view_from_view(view) == NULL) { return; } - if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= - XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { - enum wlr_edges edges = WLR_EDGE_NONE; - if (tiled) { - edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | - WLR_EDGE_BOTTOM; - } - wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); - } else { - // The version is too low for the tiled state; configure as maximized instead - // to stop the client from drawing decorations outside of the toplevel geometry. - wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); + struct wlr_xdg_surface *surface = view->wlr_xdg_surface; + enum wlr_edges edges = WLR_EDGE_NONE; + if (tiled) { + edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | + WLR_EDGE_BOTTOM; } + wlr_xdg_toplevel_set_tiled(surface, edges); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); -} - -static void set_resizing(struct sway_view *view, bool resizing) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); + struct wlr_xdg_surface *surface = view->wlr_xdg_surface; + wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); } static bool wants_floating(struct sway_view *view) { - struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; + struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; struct wlr_xdg_toplevel_state *state = &toplevel->current; return (state->min_width != 0 && state->min_height != 0 && (state->min_width == state->max_width @@ -234,17 +193,34 @@ static bool wants_floating(struct sway_view *view) { || toplevel->parent; } +static void for_each_surface(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, + user_data); +} + +static void for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data); +} + static bool is_transient_for(struct sway_view *child, struct sway_view *ancestor) { if (xdg_shell_view_from_view(child) == NULL) { return false; } - struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; - while (toplevel) { - if (toplevel->parent == ancestor->wlr_xdg_toplevel) { + struct wlr_xdg_surface *surface = child->wlr_xdg_surface; + while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { return true; } - toplevel = toplevel->parent; + surface = surface->toplevel->parent; } return false; } @@ -253,13 +229,17 @@ static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); + struct wlr_xdg_surface *surface = view->wlr_xdg_surface; + if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL + && surface->toplevel) { + wlr_xdg_toplevel_send_close(surface); + } } static void close_popups(struct sway_view *view) { struct wlr_xdg_popup *popup, *tmp; - wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { - wlr_xdg_popup_destroy(popup); + wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { + wlr_xdg_popup_destroy(popup->base); } } @@ -269,7 +249,6 @@ static void destroy(struct sway_view *view) { if (xdg_shell_view == NULL) { return; } - free(xdg_shell_view->tag); free(xdg_shell_view); } @@ -280,8 +259,9 @@ static const struct sway_view_impl view_impl = { .set_activated = set_activated, .set_tiled = set_tiled, .set_fullscreen = set_fullscreen, - .set_resizing = set_resizing, .wants_floating = wants_floating, + .for_each_surface = for_each_surface, + .for_each_popup = for_each_popup, .is_transient_for = is_transient_for, .close = _close, .close_popups = close_popups, @@ -292,60 +272,31 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit); struct sway_view *view = &xdg_shell_view->view; - struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; - - if (xdg_surface->initial_commit) { - if (view->xdg_decoration != NULL) { - set_xdg_decoration_mode(view->xdg_decoration); - } - // XXX: https://github.com/swaywm/sway/issues/2176 - wlr_xdg_surface_schedule_configure(xdg_surface); - wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel, - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); - // TODO: wlr_xdg_toplevel_set_bounds() - return; - } - - if (!xdg_surface->surface->mapped) { - return; - } - - struct wlr_box *new_geo = &xdg_surface->geometry; - bool new_size = new_geo->width != view->geometry.width || - new_geo->height != view->geometry.height || - new_geo->x != view->geometry.x || - new_geo->y != view->geometry.y; - - if (new_size) { - // The client changed its surface size in this commit. For floating - // containers, we resize the container to match. For tiling containers, - // we only recenter the surface. - memcpy(&view->geometry, new_geo, sizeof(struct wlr_box)); - if (container_is_floating(view->container)) { - view_update_size(view); - // Only set the toplevel size the current container actually has a size. - if (view->container->current.width) { - wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width, - view->geometry.height); - } - transaction_commit_dirty_client(); - } - - view_center_and_clip_surface(view); - } + struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; if (view->container->node.instruction) { - bool successful = transaction_notify_view_ready_by_serial(view, - xdg_surface->current.configure_serial); + wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry); + transaction_notify_view_ready_by_serial(view, + xdg_surface->configure_serial); + } else { + struct wlr_box new_geo; + wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); + struct sway_container *con = view->container; - // If we saved the view and this commit isn't what we're looking for - // that means the user will never actually see the buffers submitted to - // us here. Just send frame done events to these surfaces so they can - // commit another time for us. - if (view->saved_surface_tree && !successful) { - view_send_frame_done(view); + if ((new_geo.width != con->surface_width || + new_geo.height != con->surface_height)) { + // The view has unexpectedly sent a new size + desktop_damage_view(view); + view_update_size(view, new_geo.width, new_geo.height); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + desktop_damage_view(view); + transaction_commit_dirty(); + } else { + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); } } + + view_damage_from(view); } static void handle_set_title(struct wl_listener *listener, void *data) { @@ -354,70 +305,40 @@ static void handle_set_title(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; view_update_title(view, false); view_execute_criteria(view); - transaction_commit_dirty(); } static void handle_set_app_id(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, set_app_id); struct sway_view *view = &xdg_shell_view->view; - view_update_app_id(view); view_execute_criteria(view); - transaction_commit_dirty(); } static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); struct wlr_xdg_popup *wlr_popup = data; - - struct sway_xdg_popup *popup = popup_create(wlr_popup, - &xdg_shell_view->view, root->layers.popup, xdg_shell_view->image_capture_tree); - if (!popup) { - return; - } - - int lx, ly; - wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); - wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); -} - -static void handle_request_maximize(struct wl_listener *listener, void *data) { - struct sway_xdg_shell_view *xdg_shell_view = - wl_container_of(listener, xdg_shell_view, request_maximize); - struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; - if (!toplevel->base->surface->mapped) { - return; - } - wlr_xdg_surface_schedule_configure(toplevel->base); + popup_create(wlr_popup, &xdg_shell_view->view); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen); - struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; + struct wlr_xdg_toplevel_set_fullscreen_event *e = data; + struct wlr_xdg_surface *xdg_surface = + xdg_shell_view->view.wlr_xdg_surface; struct sway_view *view = &xdg_shell_view->view; - if (!toplevel->base->surface->mapped) { + if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, + "xdg_shell requested fullscreen of surface with role %i", + xdg_surface->role)) { + return; + } + if (!xdg_surface->mapped) { return; } - struct sway_container *container = view->container; - struct wlr_xdg_toplevel_requested *req = &toplevel->requested; - if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { - struct sway_output *output = req->fullscreen_output->data; - struct sway_workspace *ws = output_get_active_workspace(output); - if (ws && !container_is_scratchpad_hidden(container) && - container->pending.workspace != ws) { - if (container_is_floating(container)) { - workspace_add_floating(ws, container); - } else { - container = workspace_add_tiling(ws, container); - } - } - } - - container_set_fullscreen(container, req->fullscreen); + container_set_fullscreen(view->container, e->fullscreen); arrange_root(); transaction_commit_dirty(); @@ -427,14 +348,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_move); struct sway_view *view = &xdg_shell_view->view; - if (!container_is_floating(view->container) || - view->container->pending.fullscreen_mode) { + if (!container_is_floating(view->container)) { return; } 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); + seatop_begin_move_floating(seat, view->container, seat->last_button); } } @@ -448,7 +368,8 @@ 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, e->edges); + seatop_begin_resize_floating(seat, view->container, + seat->last_button, e->edges); } } @@ -463,8 +384,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { view_unmap(view); + wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); - wl_list_remove(&xdg_shell_view->request_maximize.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link); wl_list_remove(&xdg_shell_view->request_move.link); wl_list_remove(&xdg_shell_view->request_resize.link); @@ -476,57 +397,56 @@ static void handle_map(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map); struct sway_view *view = &xdg_shell_view->view; - struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; + struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; - view->natural_width = toplevel->base->geometry.width; - view->natural_height = toplevel->base->geometry.height; + view->natural_width = view->wlr_xdg_surface->geometry.width; + view->natural_height = view->wlr_xdg_surface->geometry.height; + if (!view->natural_width && !view->natural_height) { + view->natural_width = view->wlr_xdg_surface->surface->current.width; + view->natural_height = view->wlr_xdg_surface->surface->current.height; + } bool csd = false; - if (view->xdg_decoration) { - enum wlr_xdg_toplevel_decoration_v1_mode mode = - view->xdg_decoration->wlr_xdg_decoration->requested_mode; - csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; - } else { + if (!view->xdg_decoration) { struct sway_server_decoration *deco = - decoration_from_surface(toplevel->base->surface); + decoration_from_surface(xdg_surface->surface); csd = !deco || deco->wlr_server_decoration->mode == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; + } - view_map(view, toplevel->base->surface, - toplevel->requested.fullscreen, - toplevel->requested.fullscreen_output, - csd); + view_map(view, view->wlr_xdg_surface->surface, + xdg_surface->toplevel->client_pending.fullscreen, csd); transaction_commit_dirty(); + xdg_shell_view->commit.notify = handle_commit; + wl_signal_add(&xdg_surface->surface->events.commit, + &xdg_shell_view->commit); + xdg_shell_view->new_popup.notify = handle_new_popup; - wl_signal_add(&toplevel->base->events.new_popup, + wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_view->new_popup); - xdg_shell_view->request_maximize.notify = handle_request_maximize; - wl_signal_add(&toplevel->events.request_maximize, - &xdg_shell_view->request_maximize); - xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; - wl_signal_add(&toplevel->events.request_fullscreen, + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); xdg_shell_view->request_move.notify = handle_request_move; - wl_signal_add(&toplevel->events.request_move, + wl_signal_add(&xdg_surface->toplevel->events.request_move, &xdg_shell_view->request_move); xdg_shell_view->request_resize.notify = handle_request_resize; - wl_signal_add(&toplevel->events.request_resize, + wl_signal_add(&xdg_surface->toplevel->events.request_resize, &xdg_shell_view->request_resize); xdg_shell_view->set_title.notify = handle_set_title; - wl_signal_add(&toplevel->events.set_title, + wl_signal_add(&xdg_surface->toplevel->events.set_title, &xdg_shell_view->set_title); xdg_shell_view->set_app_id.notify = handle_set_app_id; - wl_signal_add(&toplevel->events.set_app_id, + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, &xdg_shell_view->set_app_id); } @@ -540,11 +460,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->destroy.link); wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); - wl_list_remove(&xdg_shell_view->commit.link); - view->wlr_xdg_toplevel = NULL; - if (view->xdg_decoration) { - view->xdg_decoration->view = NULL; - } + view->wlr_xdg_surface = NULL; view_begin_destroy(view); } @@ -553,12 +469,17 @@ struct sway_view *view_from_wlr_xdg_surface( return xdg_surface->data; } -void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { - struct wlr_xdg_toplevel *xdg_toplevel = data; +void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { + struct wlr_xdg_surface *xdg_surface = data; + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + sway_log(SWAY_DEBUG, "New xdg_shell popup"); + return; + } sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", - xdg_toplevel->title, xdg_toplevel->app_id); - wlr_xdg_surface_ping(xdg_toplevel->base); + xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); + wlr_xdg_surface_ping(xdg_surface); struct sway_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct sway_xdg_shell_view)); @@ -566,38 +487,17 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { return; } - if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { - free(xdg_shell_view); - return; - } - xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; + view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); + xdg_shell_view->view.wlr_xdg_surface = xdg_surface; xdg_shell_view->map.notify = handle_map; - wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map); + wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); xdg_shell_view->unmap.notify = handle_unmap; - wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap); - - xdg_shell_view->commit.notify = handle_commit; - wl_signal_add(&xdg_toplevel->base->surface->events.commit, - &xdg_shell_view->commit); + wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); xdg_shell_view->destroy.notify = handle_destroy; - wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); + wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); - wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); - xdg_shell_view->image_capture_tree = - wlr_scene_xdg_surface_create(&xdg_shell_view->view.image_capture_scene->tree, xdg_toplevel->base); - - xdg_toplevel->base->data = xdg_shell_view; -} - -void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data) { - const struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event *event = data; - struct sway_view *view = view_from_wlr_xdg_surface(event->toplevel->base); - struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); - free(xdg_shell_view->tag); - xdg_shell_view->tag = strdup(event->tag); - view_execute_criteria(view); - transaction_commit_dirty(); + xdg_surface->data = xdg_shell_view; } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c new file mode 100644 index 000000000..3eed54ab0 --- /dev/null +++ b/sway/desktop/xdg_shell_v6.c @@ -0,0 +1,495 @@ +#define _POSIX_C_SOURCE 199309L +#include +#include +#include +#include +#include +#include "log.h" +#include "sway/decoration.h" +#include "sway/desktop.h" +#include "sway/desktop/transaction.h" +#include "sway/input/cursor.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" +#include "sway/output.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" + +static const struct sway_view_child_impl popup_impl; + +static void popup_get_root_coords(struct sway_view_child *child, + int *root_sx, int *root_sy) { + struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child; + struct wlr_xdg_surface_v6 *surface = popup->wlr_xdg_surface_v6; + + int x_offset = -child->view->geometry.x - surface->geometry.x; + int y_offset = -child->view->geometry.y - surface->geometry.y; + + wlr_xdg_popup_v6_get_toplevel_coords(surface->popup, + x_offset + surface->popup->geometry.x, + y_offset + surface->popup->geometry.y, + root_sx, root_sy); +} + +static void popup_destroy(struct sway_view_child *child) { + if (!sway_assert(child->impl == &popup_impl, + "Expected an xdg_shell_v6 popup")) { + return; + } + struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child; + wl_list_remove(&popup->new_popup.link); + wl_list_remove(&popup->destroy.link); + free(popup); +} + +static const struct sway_view_child_impl popup_impl = { + .get_root_coords = popup_get_root_coords, + .destroy = popup_destroy, +}; + +static struct sway_xdg_popup_v6 *popup_create( + struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view); + +static void popup_handle_new_popup(struct wl_listener *listener, void *data) { + struct sway_xdg_popup_v6 *popup = + wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup_v6 *wlr_popup = data; + popup_create(wlr_popup, popup->child.view); +} + +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, destroy); + view_child_destroy(&popup->child); +} + +static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { + struct sway_view *view = popup->child.view; + struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; + + struct sway_output *output = view->container->workspace->output; + + // the output box expressed in the coordinate system of the toplevel parent + // of the popup + struct wlr_box output_toplevel_sx_box = { + .x = output->lx - view->container->content_x, + .y = output->ly - view->container->content_y, + .width = output->width, + .height = output->height, + }; + + wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} + +static struct sway_xdg_popup_v6 *popup_create( + struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { + struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; + + struct sway_xdg_popup_v6 *popup = + calloc(1, sizeof(struct sway_xdg_popup_v6)); + if (popup == NULL) { + return NULL; + } + view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); + popup->wlr_xdg_surface_v6 = xdg_surface; + + wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); + popup->destroy.notify = popup_handle_destroy; + + wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); + wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); + + popup_unconstrain(popup); + + return popup; +} + + +static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( + struct sway_view *view) { + if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6, + "Expected xdg_shell_v6 view")) { + return NULL; + } + return (struct sway_xdg_shell_v6_view *)view; +} + +static void get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + struct wlr_xdg_toplevel_v6_state *state = + &view->wlr_xdg_surface_v6->toplevel->current; + *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; + *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; + *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; + *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; +} + +static const char *get_string_prop(struct sway_view *view, + enum sway_view_prop prop) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return NULL; + } + switch (prop) { + case VIEW_PROP_TITLE: + return view->wlr_xdg_surface_v6->toplevel->title; + case VIEW_PROP_APP_ID: + return view->wlr_xdg_surface_v6->toplevel->app_id; + default: + return NULL; + } +} + +static uint32_t configure(struct sway_view *view, double lx, double ly, + int width, int height) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + xdg_shell_v6_view_from_view(view); + if (xdg_shell_v6_view == NULL) { + return 0; + } + return wlr_xdg_toplevel_v6_set_size( + view->wlr_xdg_surface_v6, width, height); +} + +static void set_activated(struct sway_view *view, bool activated) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + wlr_xdg_toplevel_v6_set_activated(surface, activated); + } +} + +static void set_tiled(struct sway_view *view, bool tiled) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; + wlr_xdg_toplevel_v6_set_maximized(surface, tiled); +} + +static void set_fullscreen(struct sway_view *view, bool fullscreen) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; + wlr_xdg_toplevel_v6_set_fullscreen(surface, fullscreen); +} + +static bool wants_floating(struct sway_view *view) { + struct wlr_xdg_toplevel_v6 *toplevel = + view->wlr_xdg_surface_v6->toplevel; + struct wlr_xdg_toplevel_v6_state *state = &toplevel->current; + return (state->min_width != 0 && state->min_height != 0 + && (state->min_width == state->max_width + || state->min_height == state->max_height)) + || toplevel->parent; +} + +static void for_each_surface(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_v6_for_each_surface(view->wlr_xdg_surface_v6, iterator, + user_data); +} + +static void for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator, + user_data); +} + +static bool is_transient_for(struct sway_view *child, + struct sway_view *ancestor) { + if (xdg_shell_v6_view_from_view(child) == NULL) { + return false; + } + struct wlr_xdg_surface_v6 *surface = child->wlr_xdg_surface_v6; + while (surface && surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + if (surface->toplevel->parent == ancestor->wlr_xdg_surface_v6) { + return true; + } + surface = surface->toplevel->parent; + } + return false; +} + +static void _close(struct sway_view *view) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + wlr_xdg_surface_v6_send_close(surface); + } +} + +static void close_popups_iterator(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_xdg_surface_v6 *xdg_surface_v6 = + wlr_xdg_surface_v6_from_wlr_surface(surface); + wlr_xdg_surface_v6_send_close(xdg_surface_v6); +} + +static void close_popups(struct sway_view *view) { + wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, + close_popups_iterator, NULL); +} + +static void destroy(struct sway_view *view) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + xdg_shell_v6_view_from_view(view); + if (xdg_shell_v6_view == NULL) { + return; + } + free(xdg_shell_v6_view); +} + +static const struct sway_view_impl view_impl = { + .get_constraints = get_constraints, + .get_string_prop = get_string_prop, + .configure = configure, + .set_activated = set_activated, + .set_tiled = set_tiled, + .set_fullscreen = set_fullscreen, + .wants_floating = wants_floating, + .for_each_surface = for_each_surface, + .for_each_popup = for_each_popup, + .is_transient_for = is_transient_for, + .close = _close, + .close_popups = close_popups, + .destroy = destroy, +}; + +static void handle_commit(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, commit); + struct sway_view *view = &xdg_shell_v6_view->view; + struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; + + if (view->container->node.instruction) { + wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry); + transaction_notify_view_ready_by_serial(view, + xdg_surface_v6->configure_serial); + } else { + struct wlr_box new_geo; + wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); + struct sway_container *con = view->container; + + if ((new_geo.width != con->surface_width || + new_geo.height != con->surface_height)) { + // The view has unexpectedly sent a new size + desktop_damage_view(view); + view_update_size(view, new_geo.width, new_geo.height); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + desktop_damage_view(view); + transaction_commit_dirty(); + } else { + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + } + } + + view_damage_from(view); +} + +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_title); + struct sway_view *view = &xdg_shell_v6_view->view; + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_app_id(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_app_id); + struct sway_view *view = &xdg_shell_v6_view->view; + view_execute_criteria(view); +} + +static void handle_new_popup(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, new_popup); + struct wlr_xdg_popup_v6 *wlr_popup = data; + popup_create(wlr_popup, &xdg_shell_v6_view->view); +} + +static void handle_request_fullscreen(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_fullscreen); + struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; + struct wlr_xdg_surface_v6 *xdg_surface = + xdg_shell_v6_view->view.wlr_xdg_surface_v6; + struct sway_view *view = &xdg_shell_v6_view->view; + + if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, + "xdg_shell_v6 requested fullscreen of surface with role %i", + xdg_surface->role)) { + return; + } + if (!xdg_surface->mapped) { + return; + } + + container_set_fullscreen(view->container, e->fullscreen); + + arrange_root(); + transaction_commit_dirty(); +} + +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_move); + struct sway_view *view = &xdg_shell_v6_view->view; + if (!container_is_floating(view->container)) { + return; + } + 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); + } +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_resize); + struct sway_view *view = &xdg_shell_v6_view->view; + if (!container_is_floating(view->container)) { + return; + } + 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); + } +} + +static void handle_unmap(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, unmap); + struct sway_view *view = &xdg_shell_v6_view->view; + + if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { + return; + } + + view_unmap(view); + + wl_list_remove(&xdg_shell_v6_view->commit.link); + wl_list_remove(&xdg_shell_v6_view->new_popup.link); + wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); + wl_list_remove(&xdg_shell_v6_view->request_move.link); + wl_list_remove(&xdg_shell_v6_view->request_resize.link); + wl_list_remove(&xdg_shell_v6_view->set_title.link); + wl_list_remove(&xdg_shell_v6_view->set_app_id.link); +} + +static void handle_map(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, map); + struct sway_view *view = &xdg_shell_v6_view->view; + struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6; + + view->natural_width = view->wlr_xdg_surface_v6->geometry.width; + view->natural_height = view->wlr_xdg_surface_v6->geometry.height; + if (!view->natural_width && !view->natural_height) { + view->natural_width = view->wlr_xdg_surface_v6->surface->current.width; + view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; + } + struct sway_server_decoration *deco = + decoration_from_surface(xdg_surface->surface); + bool csd = !deco || deco->wlr_server_decoration->mode + == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; + + view_map(view, view->wlr_xdg_surface_v6->surface, + xdg_surface->toplevel->client_pending.fullscreen, csd); + + transaction_commit_dirty(); + + xdg_shell_v6_view->commit.notify = handle_commit; + wl_signal_add(&xdg_surface->surface->events.commit, + &xdg_shell_v6_view->commit); + + xdg_shell_v6_view->new_popup.notify = handle_new_popup; + wl_signal_add(&xdg_surface->events.new_popup, + &xdg_shell_v6_view->new_popup); + + xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, + &xdg_shell_v6_view->request_fullscreen); + + xdg_shell_v6_view->request_move.notify = handle_request_move; + wl_signal_add(&xdg_surface->toplevel->events.request_move, + &xdg_shell_v6_view->request_move); + + xdg_shell_v6_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xdg_surface->toplevel->events.request_resize, + &xdg_shell_v6_view->request_resize); + + xdg_shell_v6_view->set_title.notify = handle_set_title; + wl_signal_add(&xdg_surface->toplevel->events.set_title, + &xdg_shell_v6_view->set_title); + + xdg_shell_v6_view->set_app_id.notify = handle_set_app_id; + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + &xdg_shell_v6_view->set_app_id); +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, destroy); + struct sway_view *view = &xdg_shell_v6_view->view; + wl_list_remove(&xdg_shell_v6_view->destroy.link); + wl_list_remove(&xdg_shell_v6_view->map.link); + wl_list_remove(&xdg_shell_v6_view->unmap.link); + view->wlr_xdg_surface_v6 = NULL; + view_begin_destroy(view); +} + +struct sway_view *view_from_wlr_xdg_surface_v6( + struct wlr_xdg_surface_v6 *xdg_surface_v6) { + return xdg_surface_v6->data; +} + +void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { + struct wlr_xdg_surface_v6 *xdg_surface = data; + + if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + sway_log(SWAY_DEBUG, "New xdg_shell_v6 popup"); + return; + } + + sway_log(SWAY_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", + xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); + wlr_xdg_surface_v6_ping(xdg_surface); + + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + calloc(1, sizeof(struct sway_xdg_shell_v6_view)); + if (!sway_assert(xdg_shell_v6_view, "Failed to allocate view")) { + return; + } + + view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); + xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; + + xdg_shell_v6_view->map.notify = handle_map; + wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); + + xdg_shell_v6_view->unmap.notify = handle_unmap; + wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_v6_view->unmap); + + xdg_shell_v6_view->destroy.notify = handle_destroy; + wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy); + + xdg_surface->data = xdg_shell_v6_view; +} diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 65016b3b1..e0d307e86 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -1,38 +1,34 @@ -#include +#define _POSIX_C_SOURCE 199309L #include #include -#include +#include #include #include -#include -#include #include -#include #include "log.h" +#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/output.h" -#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" -#include "sway/server.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" static const char *atom_map[ATOM_LAST] = { - [NET_WM_WINDOW_TYPE_NORMAL] = "_NET_WM_WINDOW_TYPE_NORMAL", - [NET_WM_WINDOW_TYPE_DIALOG] = "_NET_WM_WINDOW_TYPE_DIALOG", - [NET_WM_WINDOW_TYPE_UTILITY] = "_NET_WM_WINDOW_TYPE_UTILITY", - [NET_WM_WINDOW_TYPE_TOOLBAR] = "_NET_WM_WINDOW_TYPE_TOOLBAR", - [NET_WM_WINDOW_TYPE_SPLASH] = "_NET_WM_WINDOW_TYPE_SPLASH", - [NET_WM_WINDOW_TYPE_MENU] = "_NET_WM_WINDOW_TYPE_MENU", - [NET_WM_WINDOW_TYPE_DROPDOWN_MENU] = "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", - [NET_WM_WINDOW_TYPE_POPUP_MENU] = "_NET_WM_WINDOW_TYPE_POPUP_MENU", - [NET_WM_WINDOW_TYPE_TOOLTIP] = "_NET_WM_WINDOW_TYPE_TOOLTIP", - [NET_WM_WINDOW_TYPE_NOTIFICATION] = "_NET_WM_WINDOW_TYPE_NOTIFICATION", - [NET_WM_STATE_MODAL] = "_NET_WM_STATE_MODAL", + "_NET_WM_WINDOW_TYPE_NORMAL", + "_NET_WM_WINDOW_TYPE_DIALOG", + "_NET_WM_WINDOW_TYPE_UTILITY", + "_NET_WM_WINDOW_TYPE_TOOLBAR", + "_NET_WM_WINDOW_TYPE_SPLASH", + "_NET_WM_WINDOW_TYPE_MENU", + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", + "_NET_WM_WINDOW_TYPE_POPUP_MENU", + "_NET_WM_WINDOW_TYPE_TOOLTIP", + "_NET_WM_WINDOW_TYPE_NOTIFICATION", + "_NET_WM_STATE_MODAL", }; static void unmanaged_handle_request_configure(struct wl_listener *listener, @@ -45,12 +41,23 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, ev->width, ev->height); } -static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { +static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, set_geometry); + wl_container_of(listener, surface, commit); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); + if (xsurface->x != surface->lx || xsurface->y != surface->ly) { + // Surface has moved + desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, + true); + surface->lx = xsurface->x; + surface->ly = xsurface->y; + desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, + true); + } else { + desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, + false); + } } static void unmanaged_handle_map(struct wl_listener *listener, void *data) { @@ -58,20 +65,16 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { wl_container_of(listener, surface, map); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, - xsurface->surface); + wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); - if (surface->surface_scene) { - scene_descriptor_assign(&surface->surface_scene->buffer->node, - SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); - wlr_scene_node_set_position(&surface->surface_scene->buffer->node, - xsurface->x, xsurface->y); + wl_signal_add(&xsurface->surface->events.commit, &surface->commit); + surface->commit.notify = unmanaged_handle_commit; - wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); - surface->set_geometry.notify = unmanaged_handle_set_geometry; - } + surface->lx = xsurface->x; + surface->ly = xsurface->y; + desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); - if (wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { + if (wlr_xwayland_or_surface_wants_focus(xsurface)) { struct sway_seat *seat = input_manager_current_seat(); struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); @@ -83,24 +86,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, unmap); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - - if (surface->surface_scene) { - wl_list_remove(&surface->set_geometry.link); - - wlr_scene_node_destroy(&surface->surface_scene->buffer->node); - surface->surface_scene = NULL; - } + desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); + wl_list_remove(&surface->link); + wl_list_remove(&surface->commit.link); struct sway_seat *seat = input_manager_current_seat(); - if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { - // This simply returns focus to the parent surface if there's one available. - // This seems to handle JetBrains issues. - if (xsurface->parent && xsurface->parent->surface - && wlr_xwayland_surface_override_redirect_wants_focus(xsurface->parent)) { - seat_set_focus_surface(seat, xsurface->parent->surface, false); - return; - } - + if (seat->wlr_seat->keyboard_state.focused_surface == + xsurface->surface) { // Restore focus struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); if (previous) { @@ -111,82 +103,15 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { } } -static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { - struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, request_activate); - struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { - return; - } - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - if (focus && focus->view && focus->view->pid != xsurface->pid) { - return; - } - - seat_set_focus_surface(seat, xsurface->surface, false); -} - -static void unmanaged_handle_associate(struct wl_listener *listener, void *data) { - struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, associate); - struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - wl_signal_add(&xsurface->surface->events.map, &surface->map); - surface->map.notify = unmanaged_handle_map; - wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap); - surface->unmap.notify = unmanaged_handle_unmap; -} - -static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) { - struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, dissociate); - wl_list_remove(&surface->map.link); - wl_list_remove(&surface->unmap.link); -} - static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, destroy); - wl_list_remove(&surface->request_configure.link); - wl_list_remove(&surface->associate.link); - wl_list_remove(&surface->dissociate.link); + wl_list_remove(&surface->map.link); + wl_list_remove(&surface->unmap.link); wl_list_remove(&surface->destroy.link); - wl_list_remove(&surface->override_redirect.link); - wl_list_remove(&surface->request_activate.link); free(surface); } -static void handle_map(struct wl_listener *listener, void *data); -static void handle_associate(struct wl_listener *listener, void *data); - -struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); - -static void unmanaged_handle_override_redirect(struct wl_listener *listener, void *data) { - struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, override_redirect); - struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - - bool associated = xsurface->surface != NULL; - bool mapped = associated && xsurface->surface->mapped; - if (mapped) { - unmanaged_handle_unmap(&surface->unmap, NULL); - } - if (associated) { - unmanaged_handle_dissociate(&surface->dissociate, NULL); - } - - unmanaged_handle_destroy(&surface->destroy, NULL); - xsurface->data = NULL; - - struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); - if (associated) { - handle_associate(&xwayland_view->associate, NULL); - } - if (mapped) { - handle_map(&xwayland_view->map, xsurface); - } -} - static struct sway_xwayland_unmanaged *create_unmanaged( struct wlr_xwayland_surface *xsurface) { struct sway_xwayland_unmanaged *surface = @@ -201,20 +126,17 @@ static struct sway_xwayland_unmanaged *create_unmanaged( wl_signal_add(&xsurface->events.request_configure, &surface->request_configure); surface->request_configure.notify = unmanaged_handle_request_configure; - wl_signal_add(&xsurface->events.associate, &surface->associate); - surface->associate.notify = unmanaged_handle_associate; - wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); - surface->dissociate.notify = unmanaged_handle_dissociate; + wl_signal_add(&xsurface->events.map, &surface->map); + surface->map.notify = unmanaged_handle_map; + wl_signal_add(&xsurface->events.unmap, &surface->unmap); + surface->unmap.notify = unmanaged_handle_unmap; wl_signal_add(&xsurface->events.destroy, &surface->destroy); surface->destroy.notify = unmanaged_handle_destroy; - wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); - surface->override_redirect.notify = unmanaged_handle_override_redirect; - wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); - surface->request_activate.notify = unmanaged_handle_request_activate; return surface; } + static struct sway_xwayland_view *xwayland_view_from_view( struct sway_view *view) { if (!sway_assert(view->type == SWAY_VIEW_XWAYLAND, @@ -255,10 +177,7 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) { } return 0; case VIEW_PROP_WINDOW_TYPE: - if (view->wlr_xwayland_surface->window_type_len == 0) { - return 0; - } - return view->wlr_xwayland_surface->window_type[0]; + return *view->wlr_xwayland_surface->window_type; default: return 0; } @@ -283,11 +202,6 @@ static void set_activated(struct sway_view *view, bool activated) { return; } struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - - if (activated && surface->minimized) { - wlr_xwayland_surface_set_minimized(surface, false); - } - wlr_xwayland_surface_activate(surface, activated); } @@ -296,7 +210,7 @@ static void set_tiled(struct sway_view *view, bool tiled) { return; } struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - wlr_xwayland_surface_set_maximized(surface, tiled, tiled); + wlr_xwayland_surface_set_maximized(surface, tiled); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { @@ -328,7 +242,7 @@ static bool wants_floating(struct sway_view *view) { } } - xcb_size_hints_t *size_hints = surface->size_hints; + struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; if (size_hints != NULL && size_hints->min_width > 0 && size_hints->min_height > 0 && (size_hints->max_width == size_hints->min_width || @@ -379,27 +293,7 @@ static void destroy(struct sway_view *view) { free(xwayland_view); } -static void get_constraints(struct sway_view *view, double *min_width, - double *max_width, double *min_height, double *max_height) { - struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - xcb_size_hints_t *size_hints = surface->size_hints; - - if (size_hints == NULL) { - *min_width = DBL_MIN; - *max_width = DBL_MAX; - *min_height = DBL_MIN; - *max_height = DBL_MAX; - return; - } - - *min_width = size_hints->min_width > 0 ? size_hints->min_width : DBL_MIN; - *max_width = size_hints->max_width > 0 ? size_hints->max_width : DBL_MAX; - *min_height = size_hints->min_height > 0 ? size_hints->min_height : DBL_MIN; - *max_height = size_hints->max_height > 0 ? size_hints->max_height : DBL_MAX; -} - static const struct sway_view_impl view_impl = { - .get_constraints = get_constraints, .get_string_prop = get_string_prop, .get_int_prop = get_int_prop, .configure = configure, @@ -412,6 +306,17 @@ static const struct sway_view_impl view_impl = { .destroy = destroy, }; +static void get_geometry(struct sway_view *view, struct wlr_box *box) { + box->x = box->y = 0; + if (view->surface) { + box->width = view->surface->current.width; + box->height = view->surface->current.height; + } else { + box->width = 0; + box->height = 0; + } +} + static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit); @@ -419,38 +324,30 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; struct wlr_surface_state *state = &xsurface->surface->current; - struct wlr_box new_geo = {0}; - new_geo.width = state->width; - new_geo.height = state->height; - - bool new_size = new_geo.width != view->geometry.width || - new_geo.height != view->geometry.height; - - if (new_size) { - // The client changed its surface size in this commit. For floating - // containers, we resize the container to match. For tiling containers, - // we only recenter the surface. - memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); - if (container_is_floating(view->container)) { - view_update_size(view); - transaction_commit_dirty_client(); - } - - view_center_and_clip_surface(view); - } - if (view->container->node.instruction) { - bool successful = transaction_notify_view_ready_by_geometry(view, - xsurface->x, xsurface->y, state->width, state->height); + get_geometry(view, &view->geometry); + transaction_notify_view_ready_by_size(view, + state->width, state->height); + } else { + struct wlr_box new_geo; + get_geometry(view, &new_geo); + struct sway_container *con = view->container; - // If we saved the view and this commit isn't what we're looking for - // that means the user will never actually see the buffers submitted to - // us here. Just send frame done events to these surfaces so they can - // commit another time for us. - if (view->saved_surface_tree && !successful) { - view_send_frame_done(view); + if ((new_geo.width != con->surface_width || + new_geo.height != con->surface_height)) { + // The view has unexpectedly sent a new size + // eg. The Firefox "Save As" dialog when downloading a file + desktop_damage_view(view); + view_update_size(view, new_geo.width, new_geo.height); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + desktop_damage_view(view); + transaction_commit_dirty(); + } else { + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); } } + + view_damage_from(view); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -463,25 +360,20 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->commit.link); } - xwayland_view->view.wlr_xwayland_surface = NULL; - wl_list_remove(&xwayland_view->destroy.link); wl_list_remove(&xwayland_view->request_configure.link); wl_list_remove(&xwayland_view->request_fullscreen.link); - wl_list_remove(&xwayland_view->request_minimize.link); wl_list_remove(&xwayland_view->request_move.link); wl_list_remove(&xwayland_view->request_resize.link); wl_list_remove(&xwayland_view->request_activate.link); wl_list_remove(&xwayland_view->set_title.link); wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_role.link); - wl_list_remove(&xwayland_view->set_startup_id.link); wl_list_remove(&xwayland_view->set_window_type.link); wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->set_decorations.link); - wl_list_remove(&xwayland_view->associate.link); - wl_list_remove(&xwayland_view->dissociate.link); - wl_list_remove(&xwayland_view->override_redirect.link); + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); view_begin_destroy(&xwayland_view->view); } @@ -494,31 +386,25 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - wl_list_remove(&xwayland_view->commit.link); - wl_list_remove(&xwayland_view->surface_tree_destroy.link); - - wlr_scene_node_destroy(&xwayland_view->image_capture_scene_surface->buffer->node); - xwayland_view->image_capture_scene_surface = NULL; - - if (xwayland_view->surface_tree) { - wlr_scene_node_destroy(&xwayland_view->surface_tree->node); - xwayland_view->surface_tree = NULL; - } - view_unmap(view); -} -static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) { - struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, - surface_tree_destroy); - xwayland_view->surface_tree = NULL; + wl_list_remove(&xwayland_view->commit.link); } static void handle_map(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, map); + struct wlr_xwayland_surface *xsurface = data; struct sway_view *view = &xwayland_view->view; - struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + + if (xsurface->override_redirect) { + // This window used not to have the override redirect flag and has it + // now. Switch to unmanaged. + handle_destroy(&xwayland_view->destroy, view); + struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); + unmanaged_handle_map(&unmanaged->map, xsurface); + return; + } view->natural_width = xsurface->width; view->natural_height = xsurface->height; @@ -529,74 +415,25 @@ 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, NULL, false); - - xwayland_view->surface_tree = wlr_scene_subsurface_tree_create( - xwayland_view->view.content_tree, xsurface->surface); - - if (xwayland_view->surface_tree) { - xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy; - wl_signal_add(&xwayland_view->surface_tree->node.events.destroy, - &xwayland_view->surface_tree_destroy); - } - - xwayland_view->image_capture_scene_surface = - wlr_scene_surface_create(&xwayland_view->view.image_capture_scene->tree, xsurface->surface); + view_map(view, xsurface->surface, xsurface->fullscreen, false); transaction_commit_dirty(); } -static void handle_dissociate(struct wl_listener *listener, void *data); - -static void handle_override_redirect(struct wl_listener *listener, void *data) { - struct sway_xwayland_view *xwayland_view = - wl_container_of(listener, xwayland_view, override_redirect); - struct sway_view *view = &xwayland_view->view; - struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - - bool associated = xsurface->surface != NULL; - bool mapped = associated && xsurface->surface->mapped; - if (mapped) { - handle_unmap(&xwayland_view->unmap, NULL); - } - if (associated) { - handle_dissociate(&xwayland_view->dissociate, NULL); - } - - handle_destroy(&xwayland_view->destroy, view); - xsurface->data = NULL; - - struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); - if (associated) { - unmanaged_handle_associate(&unmanaged->associate, NULL); - } - if (mapped) { - unmanaged_handle_map(&unmanaged->map, xsurface); - } -} - static void handle_request_configure(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, request_configure); struct wlr_xwayland_surface_configure_event *ev = data; struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, ev->width, ev->height); return; } if (container_is_floating(view->container)) { - // 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->pending.content_x, - view->container->pending.content_y, - view->container->pending.content_width, - view->container->pending.content_height); - node_set_dirty(&view->container->node); + configure(view, view->container->current.content_x, + view->container->current.content_y, ev->width, ev->height); } else { configure(view, view->container->current.content_x, view->container->current.content_y, @@ -610,7 +447,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) wl_container_of(listener, xwayland_view, request_fullscreen); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } container_set_fullscreen(view->container, xsurface->fullscreen); @@ -619,35 +456,19 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) transaction_commit_dirty(); } -static void handle_request_minimize(struct wl_listener *listener, void *data) { - struct sway_xwayland_view *xwayland_view = - wl_container_of(listener, xwayland_view, request_minimize); - struct sway_view *view = &xwayland_view->view; - struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { - return; - } - - struct wlr_xwayland_minimize_event *e = data; - struct sway_seat *seat = input_manager_current_seat(); - bool focused = seat_get_focus(seat) == &view->container->node; - wlr_xwayland_surface_set_minimized(xsurface, !focused && e->minimize); -} - static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, request_move); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } - if (!container_is_floating(view->container) || - view->container->pending.fullscreen_mode) { + if (!container_is_floating(view->container)) { return; } struct sway_seat *seat = input_manager_current_seat(); - seatop_begin_move_floating(seat, view->container); + seatop_begin_move_floating(seat, view->container, seat->last_button); } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -655,7 +476,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_resize); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } if (!container_is_floating(view->container)) { @@ -663,7 +484,8 @@ 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, e->edges); + seatop_begin_resize_floating(seat, view->container, + seat->last_button, e->edges); } static void handle_request_activate(struct wl_listener *listener, void *data) { @@ -671,10 +493,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_activate); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } - view_request_activate(view, NULL); + view_request_activate(view); transaction_commit_dirty(); } @@ -684,12 +506,11 @@ static void handle_set_title(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_title); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } view_update_title(view, false); view_execute_criteria(view); - transaction_commit_dirty(); } static void handle_set_class(struct wl_listener *listener, void *data) { @@ -697,11 +518,10 @@ static void handle_set_class(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_class); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } view_execute_criteria(view); - transaction_commit_dirty(); } static void handle_set_role(struct wl_listener *listener, void *data) { @@ -709,36 +529,10 @@ static void handle_set_role(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_role); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } view_execute_criteria(view); - transaction_commit_dirty(); -} - -static void handle_set_startup_id(struct wl_listener *listener, void *data) { - struct sway_xwayland_view *xwayland_view = - wl_container_of(listener, xwayland_view, set_startup_id); - struct sway_view *view = &xwayland_view->view; - struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->startup_id == NULL) { - return; - } - - struct wlr_xdg_activation_token_v1 *token = - wlr_xdg_activation_v1_find_token( - server.xdg_activation_v1, xsurface->startup_id); - if (token == NULL) { - // Tried to activate with an unknown or expired token - return; - } - - struct launcher_ctx *ctx = token->data; - if (token->data == NULL) { - // TODO: support external launchers in X - return; - } - view_assign_ctx(view, ctx); } static void handle_set_window_type(struct wl_listener *listener, void *data) { @@ -746,11 +540,10 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_window_type); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } view_execute_criteria(view); - transaction_commit_dirty(); } static void handle_set_hints(struct wl_listener *listener, void *data) { @@ -758,58 +551,44 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_hints); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (xsurface->surface == NULL || !xsurface->surface->mapped) { + if (!xsurface->mapped) { return; } - const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); - if (!hints_urgency && view->urgent_timer) { - // The view is in the timeout period. We'll ignore the request to + if (!xsurface->hints_urgency && view->urgent_timer) { + // The view is is in the timeout period. We'll ignore the request to // unset urgency so that the view remains urgent until the timer clears // it. return; } if (view->allow_request_urgent) { - view_set_urgent(view, hints_urgency); + view_set_urgent(view, (bool)xsurface->hints_urgency); } } -static void handle_associate(struct wl_listener *listener, void *data) { - struct sway_xwayland_view *xwayland_view = - wl_container_of(listener, xwayland_view, associate); - struct wlr_xwayland_surface *xsurface = - xwayland_view->view.wlr_xwayland_surface; - wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap); - xwayland_view->unmap.notify = handle_unmap; - wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map); - xwayland_view->map.notify = handle_map; -} - -static void handle_dissociate(struct wl_listener *listener, void *data) { - struct sway_xwayland_view *xwayland_view = - wl_container_of(listener, xwayland_view, dissociate); - wl_list_remove(&xwayland_view->map.link); - wl_list_remove(&xwayland_view->unmap.link); -} - struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface) { return xsurface->data; } -struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface) { +void handle_xwayland_surface(struct wl_listener *listener, void *data) { + struct wlr_xwayland_surface *xsurface = data; + + if (xsurface->override_redirect) { + sway_log(SWAY_DEBUG, "New xwayland unmanaged surface"); + create_unmanaged(xsurface); + return; + } + sway_log(SWAY_DEBUG, "New xwayland surface title='%s' class='%s'", xsurface->title, xsurface->class); struct sway_xwayland_view *xwayland_view = calloc(1, sizeof(struct sway_xwayland_view)); if (!sway_assert(xwayland_view, "Failed to allocate view")) { - return NULL; + return; } - if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { - free(xwayland_view); - return NULL; - } + view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); xwayland_view->view.wlr_xwayland_surface = xsurface; wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); @@ -823,10 +602,6 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu &xwayland_view->request_fullscreen); xwayland_view->request_fullscreen.notify = handle_request_fullscreen; - wl_signal_add(&xsurface->events.request_minimize, - &xwayland_view->request_minimize); - xwayland_view->request_minimize.notify = handle_request_minimize; - wl_signal_add(&xsurface->events.request_activate, &xwayland_view->request_activate); xwayland_view->request_activate.notify = handle_request_activate; @@ -848,10 +623,6 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); xwayland_view->set_role.notify = handle_set_role; - wl_signal_add(&xsurface->events.set_startup_id, - &xwayland_view->set_startup_id); - xwayland_view->set_startup_id.notify = handle_set_startup_id; - wl_signal_add(&xsurface->events.set_window_type, &xwayland_view->set_window_type); xwayland_view->set_window_type.notify = handle_set_window_type; @@ -863,31 +634,13 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu &xwayland_view->set_decorations); xwayland_view->set_decorations.notify = handle_set_decorations; - wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); - xwayland_view->associate.notify = handle_associate; + wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); + xwayland_view->unmap.notify = handle_unmap; - wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); - xwayland_view->dissociate.notify = handle_dissociate; - - wl_signal_add(&xsurface->events.set_override_redirect, - &xwayland_view->override_redirect); - xwayland_view->override_redirect.notify = handle_override_redirect; + wl_signal_add(&xsurface->events.map, &xwayland_view->map); + xwayland_view->map.notify = handle_map; xsurface->data = xwayland_view; - - return xwayland_view; -} - -void handle_xwayland_surface(struct wl_listener *listener, void *data) { - struct wlr_xwayland_surface *xsurface = data; - - if (xsurface->override_redirect) { - sway_log(SWAY_DEBUG, "New xwayland unmanaged surface"); - create_unmanaged(xsurface); - return; - } - - create_xwayland_view(xsurface); } void handle_xwayland_ready(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fc0f11fd8..170532bea 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1,37 +1,58 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include #include #include -#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include #include -#include "config.h" +#include "list.h" #include "log.h" -#include "util.h" +#include "config.h" #include "sway/commands.h" +#include "sway/desktop.h" +#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" -#include "sway/input/tablet.h" #include "sway/layers.h" #include "sway/output.h" -#include "sway/scene_descriptor.h" -#include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" +static uint32_t get_current_time_msec(void) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_nsec / 1000; +} + +static struct wlr_surface *layer_surface_at(struct sway_output *output, + struct wl_list *layer, double ox, double oy, double *sx, double *sy) { + struct sway_layer_surface *sway_layer; + wl_list_for_each_reverse(sway_layer, layer, link) { + struct wlr_surface *wlr_surface = + sway_layer->layer_surface->surface; + double _sx = ox - sway_layer->geo.x; + double _sy = oy - sway_layer->geo.y; + // TODO: Test popups/subsurfaces + if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) { + *sx = _sx; + *sy = _sy; + return wlr_surface; + } + } + return NULL; +} + /** * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). @@ -39,127 +60,195 @@ struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_scene_node *scene_node = NULL; + // check for unmanaged views first +#if HAVE_XWAYLAND + struct wl_list *unmanaged = &root->xwayland_unmanaged; + struct sway_xwayland_unmanaged *unmanaged_surface; + wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { + struct wlr_xwayland_surface *xsurface = + unmanaged_surface->wlr_xwayland_surface; - struct wlr_scene_node *node; - wl_list_for_each_reverse(node, &root->layer_tree->children, link) { - struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); - - bool non_interactive = scene_descriptor_try_get(&layer->node, - SWAY_SCENE_DESC_NON_INTERACTIVE); - if (non_interactive) { - continue; - } - - scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); - if (scene_node) { - break; + double _sx = lx - unmanaged_surface->lx; + double _sy = ly - unmanaged_surface->ly; + if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { + *surface = xsurface->surface; + *sx = _sx; + *sy = _sy; + return NULL; } } - - if (scene_node) { - // determine what wlr_surface we clicked on - if (scene_node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(scene_node); - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_try_from_buffer(scene_buffer); - - if (scene_surface) { - *surface = scene_surface->surface; - } - } - - // determine what container we clicked on - struct wlr_scene_node *current = scene_node; - while (true) { - struct sway_container *con = scene_descriptor_try_get(current, - SWAY_SCENE_DESC_CONTAINER); - - if (!con) { - struct sway_view *view = scene_descriptor_try_get(current, - SWAY_SCENE_DESC_VIEW); - if (view) { - con = view->container; - } - } - - if (!con) { - struct sway_popup_desc *popup = - scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); - if (popup && popup->view) { - con = popup->view->container; - } - } - - if (con && (!con->view || con->view->surface)) { - return &con->node; - } - - if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) { - // We don't want to feed through the current workspace on - // layer shells - return NULL; - } - -#if WLR_HAS_XWAYLAND - if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { - return NULL; - } #endif - - if (!current->parent) { - break; - } - - current = ¤t->parent->node; - } - } - - // if we aren't on a container, determine what workspace we are on + // find the output the cursor is on struct wlr_output *wlr_output = wlr_output_layout_output_at( root->output_layout, lx, ly); if (wlr_output == NULL) { return NULL; } - struct sway_output *output = wlr_output->data; - if (!output || !output->enabled) { - // output is being destroyed or is being enabled + if (!output || !output->configured) { + // output is being destroyed or is being configured + return NULL; + } + double ox = lx, oy = ly; + wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); + + if (root->fullscreen_global) { + // Try fullscreen container + struct sway_container *con = tiling_container_at( + &root->fullscreen_global->node, lx, ly, surface, sx, sy); + if (con) { + return &con->node; + } return NULL; } + // find the focused workspace on the output for this seat struct sway_workspace *ws = output_get_active_workspace(output); - if (!ws) { + + if ((*surface = layer_surface_at(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + ox, oy, sx, sy))) { + return NULL; + } + if (ws->fullscreen) { + // Try transient containers + for (int i = 0; i < ws->floating->length; ++i) { + struct sway_container *floater = ws->floating->items[i]; + if (container_is_transient_for(floater, ws->fullscreen)) { + struct sway_container *con = tiling_container_at( + &floater->node, lx, ly, surface, sx, sy); + if (con) { + return &con->node; + } + } + } + // Try fullscreen container + struct sway_container *con = + tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); + if (con) { + return &con->node; + } + return NULL; + } + if ((*surface = layer_surface_at(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + ox, oy, sx, sy))) { + return NULL; + } + + struct sway_container *c; + if ((c = container_at(ws, lx, ly, surface, sx, sy))) { + return &c->node; + } + + if ((*surface = layer_surface_at(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + ox, oy, sx, sy))) { + return NULL; + } + if ((*surface = layer_surface_at(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + ox, oy, sx, sy))) { return NULL; } return &ws->node; } -void cursor_rebase(struct sway_cursor *cursor) { - uint32_t time_msec = get_current_time_in_msec(); - seatop_rebase(cursor->seat, time_msec); -} - -void cursor_rebase_all(void) { - if (!root->outputs->length) { - return; +/** + * 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; } - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - cursor_rebase(seat->cursor); + // 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; } -void cursor_update_image(struct sway_cursor *cursor, - struct sway_node *node) { - if (node && node->type == N_CONTAINER) { +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, NULL, cursor); + enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); if (edge == WLR_EDGE_NONE) { - cursor_set_image(cursor, "default", NULL); + 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 { @@ -170,28 +259,52 @@ void cursor_update_image(struct sway_cursor *cursor, } } } else { - cursor_set_image(cursor, "default", NULL); + 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); } } -static void cursor_hide(struct sway_cursor *cursor) { - wlr_cursor_unset_image(cursor->cursor); - cursor->hidden = true; - wlr_seat_pointer_notify_clear_focus(cursor->seat->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); +} + +void cursor_rebase_all(void) { + if (!root->outputs->length) { + return; + } + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + if (!seat_doing_seatop(seat)) { + cursor_rebase(seat->cursor); + } + } } static int hide_notify(void *data) { struct sway_cursor *cursor = data; - cursor_hide(cursor); + wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + cursor->hidden = true; + wlr_seat_pointer_clear_focus(cursor->seat->wlr_seat); return 1; } -int cursor_get_timeout(struct sway_cursor *cursor) { - if (cursor->pressed_button_count > 0) { - // Do not hide cursor unless all buttons are released - return 0; - } - +int cursor_get_timeout(struct sway_cursor *cursor){ struct seat_config *sc = seat_get_config(cursor->seat); if (!sc) { sc = seat_get_config_by_name("*"); @@ -203,74 +316,17 @@ int cursor_get_timeout(struct sway_cursor *cursor) { return timeout; } -void cursor_notify_key_press(struct sway_cursor *cursor) { - if (cursor->hidden) { - return; - } - - if (cursor->hide_when_typing == HIDE_WHEN_TYPING_DEFAULT) { - // No cached value, need to lookup in the seat_config - const struct seat_config *seat_config = seat_get_config(cursor->seat); - if (!seat_config) { - seat_config = seat_get_config_by_name("*"); - if (!seat_config) { - return; - } - } - cursor->hide_when_typing = seat_config->hide_cursor_when_typing; - // The default is currently disabled - if (cursor->hide_when_typing == HIDE_WHEN_TYPING_DEFAULT) { - cursor->hide_when_typing = HIDE_WHEN_TYPING_DISABLE; - } - } - - if (cursor->hide_when_typing == HIDE_WHEN_TYPING_ENABLE) { - cursor_hide(cursor); - } -} - -static enum sway_input_idle_source idle_source_from_device( - struct wlr_input_device *device) { - switch (device->type) { - case WLR_INPUT_DEVICE_KEYBOARD: - return IDLE_SOURCE_KEYBOARD; - case WLR_INPUT_DEVICE_POINTER: - return IDLE_SOURCE_POINTER; - case WLR_INPUT_DEVICE_TOUCH: - return IDLE_SOURCE_TOUCH; - case WLR_INPUT_DEVICE_TABLET: - return IDLE_SOURCE_TABLET_TOOL; - case WLR_INPUT_DEVICE_TABLET_PAD: - return IDLE_SOURCE_TABLET_PAD; - case WLR_INPUT_DEVICE_SWITCH: - return IDLE_SOURCE_SWITCH; - } - - abort(); -} - -void cursor_handle_activity_from_idle_source(struct sway_cursor *cursor, - enum sway_input_idle_source idle_source) { +void cursor_handle_activity(struct sway_cursor *cursor) { wl_event_source_timer_update( cursor->hide_source, cursor_get_timeout(cursor)); - seat_idle_notify_activity(cursor->seat, idle_source); - if (idle_source != IDLE_SOURCE_TOUCH) { + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + if (cursor->hidden) { cursor_unhide(cursor); } } -void cursor_handle_activity_from_device(struct sway_cursor *cursor, - struct wlr_input_device *device) { - enum sway_input_idle_source idle_source = idle_source_from_device(device); - cursor_handle_activity_from_idle_source(cursor, idle_source); -} - void cursor_unhide(struct sway_cursor *cursor) { - if (!cursor->hidden) { - return; - } - cursor->hidden = false; if (cursor->image_surface) { cursor_set_image_surface(cursor, @@ -284,22 +340,95 @@ void cursor_unhide(struct sway_cursor *cursor) { cursor_set_image(cursor, image, cursor->image_client); } cursor_rebase(cursor); - wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); } -void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct wlr_input_device *device, double dx, double dy, - double dx_unaccel, double dy_unaccel) { +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; + 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)time_msec * 1000, + cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, dx, dy, dx_unaccel, dy_unaccel); - // Only apply pointer constraints to real pointer input. - if (cursor->active_constraint && device->type == WLR_INPUT_DEVICE_POINTER) { - struct wlr_surface *surface = NULL; - double sx, sy; - node_at_coords(cursor->seat, + struct wlr_surface *surface = NULL; + struct sway_node *node = NULL; + double sx, sy; + if (cursor->active_constraint) { + node = node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (cursor->active_constraint->surface != surface) { @@ -316,187 +445,557 @@ void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, dy = sy_confined - sy; } - wlr_cursor_move(cursor->cursor, device, dx, dy); + wlr_cursor_move(cursor->cursor, event->device, dx, dy); - seatop_pointer_motion(cursor->seat, time_msec); + // 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(); } -static void handle_pointer_motion_relative( - struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - struct wlr_pointer_motion_event *e = data; - cursor_handle_activity_from_device(cursor, &e->pointer->base); - - pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x, - e->delta_y, e->unaccel_dx, e->unaccel_dy); -} - -static void handle_pointer_motion_absolute( - struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = - wl_container_of(listener, cursor, motion_absolute); - struct wlr_pointer_motion_absolute_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); +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); double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base, - event->x, event->y, &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); - pointer_motion(cursor, event->time_msec, &event->pointer->base, 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); + transaction_commit_dirty(); +} + +static void handle_cursor_motion_absolute( + struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = + wl_container_of(listener, cursor, motion_absolute); + struct wlr_event_pointer_motion_absolute *event = data; + + cursor_motion_absolute(cursor, event->time_msec, event->device, + event->x, event->y); +} + +/** + * 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; + } +} + +/** + * 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, + const char *identifier) { + uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | + (on_border ? BINDING_BORDER : 0) | + (on_content ? 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) || + (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; } void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, - enum wl_pointer_button_state state) { + enum wlr_button_state state) { if (time_msec == 0) { - time_msec = get_current_time_in_msec(); + 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; } - seatop_button(cursor->seat, time_msec, device, button, state); -} + // 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_titlebar = cont && !on_border && !surface; -static void handle_pointer_button(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of(listener, cursor, button); - struct wlr_pointer_button_event *event = data; + // Handle mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { - cursor->pressed_button_count++; + 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, device_identifier); } else { - if (cursor->pressed_button_count > 0) { - cursor->pressed_button_count--; - } else { - sway_log(SWAY_ERROR, "Pressed button count was wrong"); + binding = get_active_mouse_binding(cursor, + config->current_mode->mouse_bindings, modifiers, true, + on_titlebar, on_border, on_contents, 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; } } - cursor_handle_activity_from_device(cursor, &event->pointer->base); - dispatch_cursor_button(cursor, &event->pointer->base, + // 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); +} + +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); + 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_pointer_axis_event *event) { - seatop_pointer_axis(cursor->seat, event); + 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; + 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, 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, 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); + } } -static void handle_pointer_axis(struct wl_listener *listener, void *data) { +static void handle_cursor_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); - struct wlr_pointer_axis_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); + struct wlr_event_pointer_axis *event = data; + cursor_handle_activity(cursor); dispatch_cursor_axis(cursor, event); + transaction_commit_dirty(); } -static void handle_pointer_frame(struct wl_listener *listener, void *data) { +static void handle_cursor_frame(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, frame); + cursor_handle_activity(cursor); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } static void handle_touch_down(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); - struct wlr_touch_down_event *event = data; - cursor_handle_activity_from_device(cursor, &event->touch->base); - cursor_hide(cursor); + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + struct wlr_event_touch_down *event = data; struct sway_seat *seat = cursor->seat; + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct wlr_surface *surface = NULL; double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x, event->y, &lx, &ly); + double sx, sy; + node_at_coords(seat, lx, ly, &surface, &sx, &sy); seat->touch_id = event->touch_id; seat->touch_x = lx; seat->touch_y = ly; - seatop_touch_down(seat, event, lx, ly); + if (!surface) { + return; + } + + // TODO: fall back to cursor simulation if client has not bound to touch + if (seat_is_input_allowed(seat, surface)) { + wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, + event->touch_id, sx, sy); + cursor_set_image(cursor, NULL, NULL); + } } static void handle_touch_up(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); - struct wlr_touch_up_event *event = data; - cursor_handle_activity_from_device(cursor, &event->touch->base); - - struct sway_seat *seat = cursor->seat; - - if (cursor->simulating_pointer_from_touch) { - if (cursor->pointer_touch_id == cursor->seat->touch_id) { - cursor->pointer_touch_up = true; - dispatch_cursor_button(cursor, &event->touch->base, - event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); - } - } else { - seatop_touch_up(seat, event); - } -} - -static void handle_touch_cancel(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel); - struct wlr_touch_cancel_event *event = data; - cursor_handle_activity_from_device(cursor, &event->touch->base); - - struct sway_seat *seat = cursor->seat; - - if (cursor->simulating_pointer_from_touch) { - if (cursor->pointer_touch_id == cursor->seat->touch_id) { - cursor->pointer_touch_up = true; - dispatch_cursor_button(cursor, &event->touch->base, - event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); - } - } else { - seatop_touch_cancel(seat, event); - } + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + struct wlr_event_touch_up *event = data; + struct wlr_seat *seat = cursor->seat->wlr_seat; + // TODO: fall back to cursor simulation if client has not bound to touch + wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id); } static void handle_touch_motion(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_motion); - struct wlr_touch_motion_event *event = data; - cursor_handle_activity_from_device(cursor, &event->touch->base); + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + struct wlr_event_touch_motion *event = data; struct sway_seat *seat = cursor->seat; + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct wlr_surface *surface = NULL; double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x, event->y, &lx, &ly); + double sx, sy; + node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); if (seat->touch_id == event->touch_id) { seat->touch_x = lx; seat->touch_y = ly; - drag_icons_update_position(seat); + 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); + } } - if (cursor->simulating_pointer_from_touch) { - if (seat->touch_id == cursor->pointer_touch_id) { - double dx, dy; - dx = lx - cursor->cursor->x; - dy = ly - cursor->cursor->y; - pointer_motion(cursor, event->time_msec, &event->touch->base, - dx, dy, dx, dy); - } - } else { - seatop_touch_motion(seat, event, lx, ly); + if (!surface) { + return; } -} -static void handle_touch_frame(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = - wl_container_of(listener, cursor, touch_frame); - - struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; - - if (cursor->simulating_pointer_from_touch) { - wlr_seat_pointer_notify_frame(wlr_seat); - - if (cursor->pointer_touch_up) { - cursor->pointer_touch_up = false; - cursor->simulating_pointer_from_touch = false; - } - } else { - wlr_seat_touch_notify_frame(wlr_seat); + // TODO: fall back to cursor simulation if client has not bound to touch + if (seat_is_input_allowed(cursor->seat, surface)) { + wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, + event->touch_id, sx, sy); } } @@ -513,314 +1012,89 @@ static void apply_mapping_from_region(struct wlr_input_device *device, double x1 = region->x1, x2 = region->x2; double y1 = region->y1, y2 = region->y2; - if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) { - struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); - if (tablet->width_mm == 0 || tablet->height_mm == 0) { + if (region->mm) { + if (device->width_mm == 0 || device->height_mm == 0) { return; } - x1 /= tablet->width_mm; - x2 /= tablet->width_mm; - y1 /= tablet->height_mm; - y2 /= tablet->height_mm; + x1 /= device->width_mm; + x2 /= device->width_mm; + y1 /= device->height_mm; + y2 /= device->height_mm; } *x = apply_mapping_from_coord(x1, x2, *x); *y = apply_mapping_from_coord(y1, y2, *y); } -static void handle_tablet_tool_position(struct sway_cursor *cursor, - struct sway_tablet_tool *tool, - bool change_x, bool change_y, - double x, double y, double dx, double dy, - int32_t time_msec) { - - if (!change_x && !change_y) { - return; - } - - struct sway_tablet *tablet = tool->tablet; - struct sway_input_device *input_device = tablet->seat_device->input_device; - struct input_config *ic = input_device_get_config(input_device); - if (ic != NULL && ic->mapped_from_region != NULL) { - apply_mapping_from_region(input_device->wlr_device, - ic->mapped_from_region, &x, &y); - } - - switch (tool->mode) { - case SWAY_TABLET_TOOL_MODE_ABSOLUTE: - wlr_cursor_warp_absolute(cursor->cursor, input_device->wlr_device, - change_x ? x : NAN, change_y ? y : NAN); - break; - case SWAY_TABLET_TOOL_MODE_RELATIVE: - wlr_cursor_move(cursor->cursor, input_device->wlr_device, dx, dy); - break; - } - - double sx, sy; - struct wlr_surface *surface = NULL; - struct sway_seat *seat = cursor->seat; - node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - - // The logic for whether we should send a tablet event or an emulated pointer - // event is tricky. It comes down to: - // * If we began a drag on a non-tablet surface (simulating_pointer_from_tool_tip), - // then we should continue sending emulated pointer events regardless of - // whether the surface currently under us accepts tablet or not. - // * Otherwise, if we are over a surface that accepts tablet, then we should - // send tablet events. - // * If we began a drag over a tablet surface, we should continue sending - // tablet events until the drag is released, even if we are now over a - // non-tablet surface. - if (!cursor->simulating_pointer_from_tool_tip && - ((surface && wlr_surface_accepts_tablet_v2(surface, tablet->tablet_v2)) || - wlr_tablet_tool_v2_has_implicit_grab(tool->tablet_v2_tool))) { - seatop_tablet_tool_motion(seat, tool, time_msec); - } else { - wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); - pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - } -} - static void handle_tool_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); - struct wlr_tablet_tool_axis_event *event = data; - cursor_handle_activity_from_device(cursor, &event->tablet->base); + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + struct wlr_event_tablet_tool_axis *event = data; + struct sway_input_device *input_device = event->device->data; - struct sway_tablet_tool *sway_tool = event->tool->data; - if (!sway_tool) { - sway_log(SWAY_DEBUG, "tool axis before proximity"); - return; + double x = NAN, y = NAN; + if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { + x = event->x; + } + if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { + y = event->y; } - handle_tablet_tool_position(cursor, sway_tool, - event->updated_axes & WLR_TABLET_TOOL_AXIS_X, - event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, - event->x, event->y, event->dx, event->dy, event->time_msec); - - if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) { - wlr_tablet_v2_tablet_tool_notify_pressure( - sway_tool->tablet_v2_tool, event->pressure); + struct input_config *ic = input_device_get_config(input_device); + if (ic != NULL && ic->mapped_from_region != NULL) { + apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); } - if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) { - wlr_tablet_v2_tablet_tool_notify_distance( - sway_tool->tablet_v2_tool, event->distance); - } - - if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) { - sway_tool->tilt_x = event->tilt_x; - } - - if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) { - sway_tool->tilt_y = event->tilt_y; - } - - if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) { - wlr_tablet_v2_tablet_tool_notify_tilt( - sway_tool->tablet_v2_tool, - sway_tool->tilt_x, sway_tool->tilt_y); - } - - if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) { - wlr_tablet_v2_tablet_tool_notify_rotation( - sway_tool->tablet_v2_tool, event->rotation); - } - - if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) { - wlr_tablet_v2_tablet_tool_notify_slider( - sway_tool->tablet_v2_tool, event->slider); - } - - if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) { - wlr_tablet_v2_tablet_tool_notify_wheel( - sway_tool->tablet_v2_tool, event->wheel_delta, 0); - } + cursor_motion_absolute(cursor, event->time_msec, event->device, x, y); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } static void handle_tool_tip(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); - struct wlr_tablet_tool_tip_event *event = data; - cursor_handle_activity_from_device(cursor, &event->tablet->base); - - struct sway_tablet_tool *sway_tool = event->tool->data; - if (!sway_tool) { - sway_log(SWAY_DEBUG, "tool tip before proximity"); - return; - } - - struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; - struct sway_seat *seat = cursor->seat; - - - double sx, sy; - struct wlr_surface *surface = NULL; - node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, - &surface, &sx, &sy); - - if (cursor->simulating_pointer_from_tool_tip && - event->state == WLR_TABLET_TOOL_TIP_UP) { - cursor->simulating_pointer_from_tool_tip = false; - dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, - BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - } else if (!surface || !wlr_surface_accepts_tablet_v2(surface, tablet_v2)) { - // If we started holding the tool tip down on a surface that accepts - // tablet v2, we should notify that surface if it gets released over a - // surface that doesn't support v2. - if (event->state == WLR_TABLET_TOOL_TIP_UP) { - seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, - WLR_TABLET_TOOL_TIP_UP); - } else { - cursor->simulating_pointer_from_tool_tip = true; - dispatch_cursor_button(cursor, &event->tablet->base, - event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - } - } else { - seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); - } -} - -static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, - struct wlr_input_device *device) { - struct sway_tablet *tablet; - wl_list_for_each(tablet, &cursor->tablets, link) { - if (tablet->seat_device->input_device->wlr_device == device) { - return tablet; - } - } - return NULL; -} - -static void handle_tool_proximity(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = - wl_container_of(listener, cursor, tool_proximity); - struct wlr_tablet_tool_proximity_event *event = data; - cursor_handle_activity_from_device(cursor, &event->tablet->base); - - struct wlr_tablet_tool *tool = event->tool; - if (!tool->data) { - struct sway_tablet *tablet = get_tablet_for_device(cursor, - &event->tablet->base); - if (!tablet) { - sway_log(SWAY_ERROR, "no tablet for tablet tool"); - return; - } - sway_tablet_tool_configure(tablet, tool); - } - - struct sway_tablet_tool *sway_tool = tool->data; - if (!sway_tool) { - sway_log(SWAY_ERROR, "tablet tool not initialized"); - return; - } - - if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) { - wlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool); - return; - } - - handle_tablet_tool_position(cursor, sway_tool, true, true, event->x, event->y, - 0, 0, event->time_msec); + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + struct wlr_event_tablet_tool_tip *event = data; + dispatch_cursor_button(cursor, event->device, event->time_msec, + BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? + WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + transaction_commit_dirty(); } static void handle_tool_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); - struct wlr_tablet_tool_button_event *event = data; - cursor_handle_activity_from_device(cursor, &event->tablet->base); - - struct sway_tablet_tool *sway_tool = event->tool->data; - if (!sway_tool) { - sway_log(SWAY_DEBUG, "tool button before proximity"); - return; - } - struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; - - double sx, sy; - struct wlr_surface *surface = NULL; - - node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, - &surface, &sx, &sy); - - // TODO: floating resize should support graphics tablet events - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - bool mod_pressed = modifiers & config->floating_mod; - - bool surface_supports_tablet_events = - surface && wlr_surface_accepts_tablet_v2(surface, tablet_v2); - - // Simulate pointer when: - // 1. The modifier key is pressed, OR - // 2. The surface under the cursor does not support tablet events. - bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events; - - // Similar to tool tip, we need to selectively simulate mouse events, but we - // want to make sure that it is always consistent. Because all tool buttons - // currently map to BTN_RIGHT, we need to keep count of how many tool - // buttons are currently pressed down so we can send consistent events. - // - // The logic follows: - // - If we are already simulating the pointer, we should continue to do so - // until at least no tool button is held down. - // - If we should simulate the pointer and no tool button is currently held - // down, begin simulating the pointer. - // - If neither of the above are true, send the tablet events. - if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button) - || (cursor->tool_buttons == 0 && should_simulate_pointer)) { - cursor->simulating_pointer_from_tool_button = true; - - // TODO: the user may want to configure which tool buttons are mapped to - // which simulated pointer buttons - switch (event->state) { - case WLR_BUTTON_PRESSED: - if (cursor->tool_buttons == 0) { - dispatch_cursor_button(cursor, &event->tablet->base, - event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); - } - break; - case WLR_BUTTON_RELEASED: - if (cursor->tool_buttons <= 1) { - dispatch_cursor_button(cursor, &event->tablet->base, - event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED); - } - break; - } - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - } else { - cursor->simulating_pointer_from_tool_button = false; - - wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, - event->button, (enum zwp_tablet_pad_v2_button_state)event->state); - } - - // Update tool button count. + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + struct wlr_event_tablet_tool_button *event = data; + // TODO: the user may want to configure which tool buttons are mapped to + // which simulated pointer buttons switch (event->state) { case WLR_BUTTON_PRESSED: + if (cursor->tool_buttons == 0) { + dispatch_cursor_button(cursor, event->device, + event->time_msec, BTN_RIGHT, event->state); + } cursor->tool_buttons++; break; case WLR_BUTTON_RELEASED: - if (cursor->tool_buttons == 0) { - sway_log(SWAY_ERROR, "inconsistent tablet tool button events"); - } else { - cursor->tool_buttons--; + if (cursor->tool_buttons == 1) { + dispatch_cursor_button(cursor, event->device, + event->time_msec, BTN_RIGHT, event->state); } + cursor->tool_buttons--; break; } + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + transaction_commit_dirty(); } static void check_constraint_region(struct sway_cursor *cursor) { struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; pixman_region32_t *region = &constraint->region; struct sway_view *view = view_from_wlr_surface(constraint->surface); - if (cursor->active_confine_requires_warp && view) { - cursor->active_confine_requires_warp = false; - + if (view) { struct sway_container *con = view->container; - double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x; - double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y; + double sx = cursor->cursor->x - con->content_x + view->geometry.x; + double sy = cursor->cursor->y - con->content_y + view->geometry.y; if (!pixman_region32_contains_point(region, floor(sx), floor(sy), NULL)) { @@ -831,10 +1105,8 @@ static void check_constraint_region(struct sway_cursor *cursor) { double sy = (boxes[0].y1 + boxes[0].y2) / 2.; wlr_cursor_warp_closest(cursor->cursor, NULL, - sx + con->pending.content_x - view->geometry.x, - sy + con->pending.content_y - view->geometry.y); - - cursor_rebase(cursor); + sx + con->content_x - view->geometry.x, + sy + con->content_y - view->geometry.y); } } } @@ -857,20 +1129,11 @@ static void handle_constraint_commit(struct wl_listener *listener, check_constraint_region(cursor); } -static void handle_pointer_constraint_set_region(struct wl_listener *listener, - void *data) { - struct sway_pointer_constraint *sway_constraint = - wl_container_of(listener, sway_constraint, set_region); - struct sway_cursor *cursor = sway_constraint->cursor; - - cursor->active_confine_requires_warp = true; -} - -static void handle_request_pointer_set_cursor(struct wl_listener *listener, +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 (!seatop_allows_set_cursor(cursor->seat)) { + if (seat_doing_seatop(cursor->seat)) { return; } struct wlr_seat_pointer_request_set_cursor_event *event = data; @@ -893,89 +1156,6 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener, event->hotspot_y, focused_client); } -static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, hold_begin); - struct wlr_pointer_hold_begin_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_hold_begin(cursor->seat, event); -} - -static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, hold_end); - struct wlr_pointer_hold_end_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_hold_end(cursor->seat, event); -} - -static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, pinch_begin); - struct wlr_pointer_pinch_begin_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_pinch_begin(cursor->seat, event); -} - -static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, pinch_update); - struct wlr_pointer_pinch_update_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_pinch_update(cursor->seat, event); -} - -static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, pinch_end); - struct wlr_pointer_pinch_end_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_pinch_end(cursor->seat, event); -} - -static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, swipe_begin); - struct wlr_pointer_swipe_begin_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_swipe_begin(cursor->seat, event); -} - -static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, swipe_update); - struct wlr_pointer_swipe_update_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_swipe_update(cursor->seat, event); -} - -static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of( - listener, cursor, swipe_end); - struct wlr_pointer_swipe_end_event *event = data; - cursor_handle_activity_from_device(cursor, &event->pointer->base); - seatop_swipe_end(cursor->seat, event); -} - -static void handle_image_surface_destroy(struct wl_listener *listener, - void *data) { - struct sway_cursor *cursor = - wl_container_of(listener, cursor, image_surface_destroy); - cursor_set_image(cursor, NULL, cursor->image_client); - cursor_rebase(cursor); -} - -static void set_image_surface(struct sway_cursor *cursor, - struct wlr_surface *surface) { - wl_list_remove(&cursor->image_surface_destroy.link); - cursor->image_surface = surface; - if (surface) { - wl_signal_add(&surface->events.destroy, &cursor->image_surface_destroy); - } else { - wl_list_init(&cursor->image_surface_destroy.link); - } -} - void cursor_set_image(struct sway_cursor *cursor, const char *image, struct wl_client *client) { if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { @@ -983,8 +1163,8 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, } const char *current_image = cursor->image; - set_image_surface(cursor, NULL); cursor->image = image; + cursor->image_surface = NULL; cursor->hotspot_x = cursor->hotspot_y = 0; cursor->image_client = client; @@ -993,9 +1173,10 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, } if (!image) { - wlr_cursor_unset_image(cursor->cursor); + wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); } else if (!current_image || strcmp(current_image, image) != 0) { - wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image); + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, + cursor->cursor); } } @@ -1006,8 +1187,8 @@ void cursor_set_image_surface(struct sway_cursor *cursor, return; } - set_image_surface(cursor, surface); cursor->image = NULL; + cursor->image_surface = surface; cursor->hotspot_x = hotspot_x; cursor->hotspot_y = hotspot_y; cursor->image_client = client; @@ -1026,15 +1207,6 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { wl_event_source_remove(cursor->hide_source); - wl_list_remove(&cursor->image_surface_destroy.link); - wl_list_remove(&cursor->hold_begin.link); - wl_list_remove(&cursor->hold_end.link); - wl_list_remove(&cursor->pinch_begin.link); - wl_list_remove(&cursor->pinch_update.link); - wl_list_remove(&cursor->pinch_end.link); - wl_list_remove(&cursor->swipe_begin.link); - wl_list_remove(&cursor->swipe_update.link); - wl_list_remove(&cursor->swipe_end.link); wl_list_remove(&cursor->motion.link); wl_list_remove(&cursor->motion_absolute.link); wl_list_remove(&cursor->button.link); @@ -1042,12 +1214,9 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { wl_list_remove(&cursor->frame.link); wl_list_remove(&cursor->touch_down.link); wl_list_remove(&cursor->touch_up.link); - wl_list_remove(&cursor->touch_cancel.link); wl_list_remove(&cursor->touch_motion.link); - wl_list_remove(&cursor->touch_frame.link); wl_list_remove(&cursor->tool_axis.link); wl_list_remove(&cursor->tool_tip.link); - wl_list_remove(&cursor->tool_proximity.link); wl_list_remove(&cursor->tool_button.link); wl_list_remove(&cursor->request_set_cursor.link); @@ -1077,44 +1246,22 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { cursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop, hide_notify, cursor); - wl_list_init(&cursor->image_surface_destroy.link); - cursor->image_surface_destroy.notify = handle_image_surface_destroy; - - wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); - cursor->hold_begin.notify = handle_pointer_hold_begin; - wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); - cursor->hold_end.notify = handle_pointer_hold_end; - - wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); - cursor->pinch_begin.notify = handle_pointer_pinch_begin; - wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); - cursor->pinch_update.notify = handle_pointer_pinch_update; - wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); - cursor->pinch_end.notify = handle_pointer_pinch_end; - - wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); - cursor->swipe_begin.notify = handle_pointer_swipe_begin; - wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); - cursor->swipe_update.notify = handle_pointer_swipe_update; - wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); - cursor->swipe_end.notify = handle_pointer_swipe_end; - // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); - cursor->motion.notify = handle_pointer_motion_relative; + cursor->motion.notify = handle_cursor_motion; wl_signal_add(&wlr_cursor->events.motion_absolute, &cursor->motion_absolute); - cursor->motion_absolute.notify = handle_pointer_motion_absolute; + cursor->motion_absolute.notify = handle_cursor_motion_absolute; wl_signal_add(&wlr_cursor->events.button, &cursor->button); - cursor->button.notify = handle_pointer_button; + cursor->button.notify = handle_cursor_button; wl_signal_add(&wlr_cursor->events.axis, &cursor->axis); - cursor->axis.notify = handle_pointer_axis; + cursor->axis.notify = handle_cursor_axis; wl_signal_add(&wlr_cursor->events.frame, &cursor->frame); - cursor->frame.notify = handle_pointer_frame; + cursor->frame.notify = handle_cursor_frame; wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down); cursor->touch_down.notify = handle_touch_down; @@ -1122,16 +1269,13 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); cursor->touch_up.notify = handle_touch_up; - wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel); - cursor->touch_cancel.notify = handle_touch_cancel; - wl_signal_add(&wlr_cursor->events.touch_motion, &cursor->touch_motion); cursor->touch_motion.notify = handle_touch_motion; - wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame); - cursor->touch_frame.notify = handle_touch_frame; - + // TODO: tablet protocol support + // Note: We should emulate pointer events for clients that don't support the + // tablet protocol when the time comes wl_signal_add(&wlr_cursor->events.tablet_tool_axis, &cursor->tool_axis); cursor->tool_axis.notify = handle_tool_axis; @@ -1139,19 +1283,14 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip); cursor->tool_tip.notify = handle_tool_tip; - wl_signal_add(&wlr_cursor->events.tablet_tool_proximity, &cursor->tool_proximity); - cursor->tool_proximity.notify = handle_tool_proximity; - wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button); cursor->tool_button.notify = handle_tool_button; wl_signal_add(&seat->wlr_seat->events.request_set_cursor, &cursor->request_set_cursor); - cursor->request_set_cursor.notify = handle_request_pointer_set_cursor; + cursor->request_set_cursor.notify = handle_request_set_cursor; wl_list_init(&cursor->constraint_commit.link); - wl_list_init(&cursor->tablets); - wl_list_init(&cursor->tablet_pads); cursor->cursor = wlr_cursor; @@ -1160,27 +1299,25 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { /** * Warps the cursor to the middle of the container argument. - * Does nothing if the cursor is already inside the container and `force` is - * false. If container is NULL, returns without doing anything. + * Does nothing if the cursor is already inside the container. + * If container is NULL, returns without doing anything. */ void cursor_warp_to_container(struct sway_cursor *cursor, - struct sway_container *container, bool force) { + struct sway_container *container) { if (!container) { return; } struct wlr_box box; container_get_box(container, &box); - if (!force && wlr_box_contains_point(&box, cursor->cursor->x, - cursor->cursor->y)) { + if (wlr_box_contains_point(&box, cursor->cursor->x, cursor->cursor->y)) { return; } - double x = container->pending.x + container->pending.width / 2.0; - double y = container->pending.y + container->pending.height / 2.0; + double x = container->x + container->width / 2.0; + double y = container->y + container->height / 2.0; wlr_cursor_warp(cursor->cursor, NULL, x, y); - cursor_unhide(cursor); } /** @@ -1197,7 +1334,6 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor, double y = workspace->y + workspace->height / 2.0; wlr_cursor_warp(cursor->cursor, NULL, x, y); - cursor_unhide(cursor); } uint32_t get_mouse_bindsym(const char *name, char **error) { @@ -1213,11 +1349,15 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; return buttons[number - 1]; - } else if (has_prefix(name, "BTN_")) { + } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { // Get event code from name int code = libevdev_event_code_from_name(EV_KEY, name); if (code == -1) { - *error = format_str("Unknown event %s", name); + size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Unknown event %s", name); + } return 0; } return code; @@ -1238,9 +1378,14 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { return 0; } const char *event = libevdev_event_code_get_name(EV_KEY, code); - if (!event || !has_prefix(event, "BTN_")) { - *error = format_str("Event code %d (%s) is not a button", - code, event ? event : "(null)"); + if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { + size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", + code, event) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Event code %d (%s) is not a button", + code, event); + } return 0; } return code; @@ -1273,25 +1418,18 @@ const char *get_mouse_button_name(uint32_t button) { static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; - if (constraint->current.cursor_hint.enabled) { + if (constraint->current.committed & + WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { double sx = constraint->current.cursor_hint.x; double sy = constraint->current.cursor_hint.y; struct sway_view *view = view_from_wlr_surface(constraint->surface); - if (!view) { - return; - } - struct sway_container *con = view->container; - double lx = sx + con->pending.content_x - view->geometry.x; - double ly = sy + con->pending.content_y - view->geometry.y; + double lx = sx + con->content_x - view->geometry.x; + double ly = sy + con->content_y - view->geometry.y; wlr_cursor_warp(cursor->cursor, NULL, lx, ly); - - // Warp the pointer as well, so that on the next pointer rebase we don't - // send an unexpected synthetic motion event to clients. - wlr_seat_pointer_warp(constraint->seat, sx, sy); } } @@ -1299,9 +1437,9 @@ void handle_constraint_destroy(struct wl_listener *listener, void *data) { struct sway_pointer_constraint *sway_constraint = wl_container_of(listener, sway_constraint, destroy); struct wlr_pointer_constraint_v1 *constraint = data; - struct sway_cursor *cursor = sway_constraint->cursor; + struct sway_seat *seat = constraint->seat->data; + struct sway_cursor *cursor = seat->cursor; - wl_list_remove(&sway_constraint->set_region.link); wl_list_remove(&sway_constraint->destroy.link); if (cursor->active_constraint == constraint) { @@ -1323,18 +1461,17 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { struct sway_pointer_constraint *sway_constraint = calloc(1, sizeof(struct sway_pointer_constraint)); - sway_constraint->cursor = seat->cursor; sway_constraint->constraint = constraint; - sway_constraint->set_region.notify = handle_pointer_constraint_set_region; - wl_signal_add(&constraint->events.set_region, &sway_constraint->set_region); - sway_constraint->destroy.notify = handle_constraint_destroy; wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); - struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; - if (surface && surface == constraint->surface) { - sway_cursor_constrain(seat->cursor, constraint); + struct sway_node *focus = seat_get_focus(seat); + if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { + struct wlr_surface *surface = focus->sway_container->view->surface; + if (surface == constraint->surface) { + sway_cursor_constrain(seat->cursor, constraint); + } } } @@ -1369,8 +1506,6 @@ void sway_cursor_constrain(struct sway_cursor *cursor, return; } - cursor->active_confine_requires_warp = true; - // FIXME: Big hack, stolen from wlr_pointer_constraints_v1.c:121. // This is necessary because the focus may be set before the surface // has finished committing, which means that warping won't work properly, @@ -1392,26 +1527,3 @@ void sway_cursor_constrain(struct sway_cursor *cursor, wl_signal_add(&constraint->surface->events.commit, &cursor->constraint_commit); } - -void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) { - const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; - struct sway_seat *seat = event->seat_client->seat->data; - - if (!seatop_allows_set_cursor(seat)) { - return; - } - - struct wl_client *focused_client = NULL; - struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface; - if (focused_surface != NULL) { - focused_client = wl_resource_get_client(focused_surface->resource); - } - - // TODO: check cursor mode - if (focused_client == NULL || event->seat_client->client != focused_client) { - sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); - return; - } - - cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client); -} diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index ffcf8fc52..f99fc395b 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -1,30 +1,22 @@ +#define _POSIX_C_SOURCE 200809L #include +#include +#include #include #include +#include #include -#include -#include -#include -#include +#include +#include #include -#include #include "sway/config.h" -#include "sway/input/cursor.h" #include "sway/input/input-manager.h" -#include "sway/input/keyboard.h" -#include "sway/input/libinput.h" #include "sway/input/seat.h" -#include "sway/ipc-server.h" #include "sway/server.h" -#include "sway/tree/view.h" #include "stringop.h" #include "list.h" #include "log.h" -#if WLR_HAS_LIBINPUT_BACKEND -#include -#endif - #define DEFAULT_SEAT "seat0" struct input_config *current_input_config = NULL; @@ -53,113 +45,32 @@ struct sway_seat *input_manager_get_seat(const char *seat_name, bool create) { return create ? seat_create(seat_name) : NULL; } -struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_seat) { - struct sway_seat *seat = NULL; - - wl_list_for_each(seat, &server.input->seats, link) { - if (seat->wlr_seat == wlr_seat) { - return seat; - } - } - - return NULL; -} - char *input_device_get_identifier(struct wlr_input_device *device) { - int vendor = 0, product = 0; -#if WLR_HAS_LIBINPUT_BACKEND - if (wlr_input_device_is_libinput(device)) { - struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device); - vendor = libinput_device_get_id_vendor(libinput_dev); - product = libinput_device_get_id_product(libinput_dev); - } -#endif - - char *name = strdup(device->name ? device->name : ""); + int vendor = device->vendor; + int product = device->product; + char *name = strdup(device->name); strip_whitespace(name); char *p = name; for (; *p; ++p) { - // There are in fact input devices with unprintable characters in its name - if (*p == ' ' || !isprint(*p)) { + if (*p == ' ') { *p = '_'; } } - char *identifier = format_str("%d:%d:%s", vendor, product, name); + const char *fmt = "%d:%d:%s"; + int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; + char *identifier = malloc(len); + if (!identifier) { + sway_log(SWAY_ERROR, "Unable to allocate unique input device name"); + return NULL; + } + + snprintf(identifier, len, fmt, vendor, product, name); free(name); return identifier; } -static bool device_is_touchpad(struct sway_input_device *device) { -#if WLR_HAS_LIBINPUT_BACKEND - 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; -#else - return false; -#endif -} - -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: - 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; @@ -196,6 +107,323 @@ void input_manager_verify_fallback_seat(void) { } } +static void log_libinput_config_status(enum libinput_config_status status) { + if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "Failed to apply libinput config: %s", + libinput_config_status_to_str(status)); + } +} + +static void input_manager_libinput_config_keyboard( + 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_keyboard(%s)", + ic->identifier); + + if (ic->send_events != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_keyboard(%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_keyboard( + 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_keyboard(%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; + 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_touch(%s)", + ic->identifier); + + if (ic->send_events != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_touch(%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_touch( + 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_touch(%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_pointer( + 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_pointer(%s)", + ic->identifier); + + if (ic->accel_profile != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", + ic->identifier, ic->accel_profile); + log_libinput_config_status(libinput_device_config_accel_set_profile( + libinput_device, ic->accel_profile)); + } + if (ic->click_method != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", + ic->identifier, ic->click_method); + log_libinput_config_status(libinput_device_config_click_set_method( + libinput_device, ic->click_method)); + } + if (ic->drag != INT_MIN) { + sway_log(SWAY_DEBUG, + "libinput_config_pointer(%s) tap_set_drag_enabled(%d)", + ic->identifier, ic->drag); + log_libinput_config_status(libinput_device_config_tap_set_drag_enabled( + libinput_device, ic->drag)); + } + if (ic->drag_lock != INT_MIN) { + sway_log(SWAY_DEBUG, + "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", + ic->identifier, ic->drag_lock); + log_libinput_config_status( + libinput_device_config_tap_set_drag_lock_enabled( + libinput_device, ic->drag_lock)); + } + if (ic->dwt != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", + ic->identifier, ic->dwt); + log_libinput_config_status(libinput_device_config_dwt_set_enabled( + libinput_device, ic->dwt)); + } + if (ic->left_handed != INT_MIN) { + sway_log(SWAY_DEBUG, + "libinput_config_pointer(%s) left_handed_set_enabled(%d)", + ic->identifier, ic->left_handed); + log_libinput_config_status(libinput_device_config_left_handed_set( + libinput_device, ic->left_handed)); + } + if (ic->middle_emulation != INT_MIN) { + sway_log(SWAY_DEBUG, + "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)", + ic->identifier, ic->middle_emulation); + log_libinput_config_status( + libinput_device_config_middle_emulation_set_enabled( + libinput_device, ic->middle_emulation)); + } + if (ic->natural_scroll != INT_MIN) { + sway_log(SWAY_DEBUG, + "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)", + ic->identifier, ic->natural_scroll); + log_libinput_config_status( + libinput_device_config_scroll_set_natural_scroll_enabled( + libinput_device, ic->natural_scroll)); + } + if (ic->pointer_accel != FLT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", + ic->identifier, ic->pointer_accel); + log_libinput_config_status(libinput_device_config_accel_set_speed( + libinput_device, ic->pointer_accel)); + } + if (ic->scroll_button != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) scroll_set_button(%d)", + ic->identifier, ic->scroll_button); + log_libinput_config_status(libinput_device_config_scroll_set_button( + libinput_device, ic->scroll_button)); + } + if (ic->scroll_method != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", + ic->identifier, ic->scroll_method); + log_libinput_config_status(libinput_device_config_scroll_set_method( + libinput_device, ic->scroll_method)); + } + if (ic->send_events != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%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)); + } + if (ic->tap != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", + ic->identifier, ic->tap); + log_libinput_config_status(libinput_device_config_tap_set_enabled( + libinput_device, ic->tap)); + } + if (ic->tap_button_map != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", + ic->identifier, ic->tap_button_map); + log_libinput_config_status(libinput_device_config_tap_set_button_map( + libinput_device, ic->tap_button_map)); + } +} + +static void input_manager_libinput_reset_pointer( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + + if (!wlr_input_device_is_libinput(wlr_device)) { + return; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(wlr_device); + + enum libinput_config_accel_profile accel_profile = + libinput_device_config_accel_get_default_profile(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) accel_set_profile(%d)", + input_device->identifier, accel_profile); + log_libinput_config_status(libinput_device_config_accel_set_profile( + libinput_device, accel_profile)); + + enum libinput_config_click_method click_method = + libinput_device_config_click_get_default_method(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) click_set_method(%d)", + input_device->identifier, click_method); + log_libinput_config_status(libinput_device_config_click_set_method( + libinput_device, click_method)); + + enum libinput_config_drag_state drag = + libinput_device_config_tap_get_default_drag_enabled(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) tap_set_drag_enabled(%d)", + input_device->identifier, drag); + log_libinput_config_status(libinput_device_config_tap_set_drag_enabled( + libinput_device, drag)); + + enum libinput_config_drag_lock_state drag_lock = + libinput_device_config_tap_get_default_drag_lock_enabled( + libinput_device); + sway_log(SWAY_DEBUG, + "libinput_reset_pointer(%s) tap_set_drag_lock_enabled(%d)", + input_device->identifier, drag_lock); + log_libinput_config_status( + libinput_device_config_tap_set_drag_lock_enabled( + libinput_device, drag_lock)); + + enum libinput_config_dwt_state dwt = + libinput_device_config_dwt_get_default_enabled(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) dwt_set_enabled(%d)", + input_device->identifier, dwt); + log_libinput_config_status(libinput_device_config_dwt_set_enabled( + libinput_device, dwt)); + + int left_handed = + libinput_device_config_left_handed_get_default(libinput_device); + sway_log(SWAY_DEBUG, + "libinput_reset_pointer(%s) left_handed_set_enabled(%d)", + input_device->identifier, left_handed); + log_libinput_config_status(libinput_device_config_left_handed_set( + libinput_device, left_handed)); + + enum libinput_config_middle_emulation_state middle_emulation = + libinput_device_config_middle_emulation_get_default_enabled( + libinput_device); + sway_log(SWAY_DEBUG, + "libinput_reset_pointer(%s) middle_emulation_set_enabled(%d)", + input_device->identifier, middle_emulation); + log_libinput_config_status( + libinput_device_config_middle_emulation_set_enabled( + libinput_device, middle_emulation)); + + int natural_scroll = + libinput_device_config_scroll_get_default_natural_scroll_enabled( + libinput_device); + sway_log(SWAY_DEBUG, + "libinput_reset_pointer(%s) natural_scroll_set_enabled(%d)", + input_device->identifier, natural_scroll); + log_libinput_config_status( + libinput_device_config_scroll_set_natural_scroll_enabled( + libinput_device, natural_scroll)); + + double pointer_accel = + libinput_device_config_accel_get_default_speed(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) accel_set_speed(%f)", + input_device->identifier, pointer_accel); + log_libinput_config_status(libinput_device_config_accel_set_speed( + libinput_device, pointer_accel)); + + uint32_t scroll_button = + libinput_device_config_scroll_get_default_button(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) scroll_set_button(%d)", + input_device->identifier, scroll_button); + log_libinput_config_status(libinput_device_config_scroll_set_button( + libinput_device, scroll_button)); + + enum libinput_config_scroll_method scroll_method = + libinput_device_config_scroll_get_default_method(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) scroll_set_method(%d)", + input_device->identifier, scroll_method); + log_libinput_config_status(libinput_device_config_scroll_set_method( + libinput_device, scroll_method)); + + uint32_t send_events = + libinput_device_config_send_events_get_default_mode(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%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)); + + enum libinput_config_tap_state tap = + libinput_device_config_tap_get_default_enabled(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) tap_set_enabled(%d)", + input_device->identifier, tap); + log_libinput_config_status(libinput_device_config_tap_set_enabled( + libinput_device, tap)); + + enum libinput_config_tap_button_map tap_button_map = + libinput_device_config_tap_get_button_map(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_pointer(%s) tap_set_button_map(%d)", + input_device->identifier, tap_button_map); + log_libinput_config_status(libinput_device_config_tap_set_button_map( + libinput_device, tap_button_map)); +} + static void handle_device_destroy(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; @@ -213,8 +441,6 @@ static void handle_device_destroy(struct wl_listener *listener, void *data) { seat_remove_device(seat, input_device); } - ipc_event_input("removed", input_device); - wl_list_remove(&input_device->link); wl_list_remove(&input_device->device_destroy.link); free(input_device->identifier); @@ -240,13 +466,14 @@ 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 WLR_HAS_LIBINPUT_BACKEND - bool config_changed = sway_input_configure_libinput_device(input_device); -#else - bool config_changed = false; -#endif + 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_TOUCH) { + input_manager_libinput_config_touch(input_device); + } wl_signal_add(&device->events.destroy, &input_device->device_destroy); input_device->device_destroy.notify = handle_device_destroy; @@ -282,98 +509,41 @@ static void handle_new_input(struct wl_listener *listener, void *data) { "device '%s' is not configured on any seats", input_device->identifier); } +} - ipc_event_input("added", input_device); - - if (config_changed) { - ipc_event_input("libinput_config", input_device); +static void handle_inhibit_activate(struct wl_listener *listener, void *data) { + struct sway_input_manager *input_manager = wl_container_of( + listener, input_manager, inhibit_activate); + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + seat_set_exclusive_client(seat, input_manager->inhibit->active_client); } } -static void handle_keyboard_shortcuts_inhibitor_destroy( - struct wl_listener *listener, void *data) { - struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = - wl_container_of(listener, sway_inhibitor, destroy); - - sway_log(SWAY_DEBUG, "Removing keyboard shortcuts inhibitor"); - - // sway_seat::keyboard_shortcuts_inhibitors - wl_list_remove(&sway_inhibitor->link); - wl_list_remove(&sway_inhibitor->destroy.link); - free(sway_inhibitor); -} - -static void handle_keyboard_shortcuts_inhibit_new_inhibitor( - struct wl_listener *listener, void *data) { - struct sway_input_manager *input_manager = - wl_container_of(listener, input_manager, - keyboard_shortcuts_inhibit_new_inhibitor); - struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = data; - - sway_log(SWAY_DEBUG, "Adding keyboard shortcuts inhibitor"); - - struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = - calloc(1, sizeof(struct sway_keyboard_shortcuts_inhibitor)); - if (!sway_assert(sway_inhibitor, "could not allocate keyboard " - "shortcuts inhibitor")) { - return; - } - sway_inhibitor->inhibitor = inhibitor; - - sway_inhibitor->destroy.notify = handle_keyboard_shortcuts_inhibitor_destroy; - wl_signal_add(&inhibitor->events.destroy, &sway_inhibitor->destroy); - - // attach inhibitor to the seat it applies to - struct sway_seat *seat = inhibitor->seat->data; - wl_list_insert(&seat->keyboard_shortcuts_inhibitors, &sway_inhibitor->link); - - // per-view, seat-agnostic config via criteria - struct sway_view *view = view_from_wlr_surface(inhibitor->surface); - enum seat_config_shortcuts_inhibit inhibit = SHORTCUTS_INHIBIT_DEFAULT; - if (view) { - inhibit = view->shortcuts_inhibit; - } - - if (inhibit == SHORTCUTS_INHIBIT_DEFAULT) { - struct seat_config *config = seat_get_config(seat); - if (!config) { - config = seat_get_config_by_name("*"); - } - - if (config) { - inhibit = config->shortcuts_inhibit; +static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { + struct sway_input_manager *input_manager = wl_container_of( + listener, input_manager, inhibit_deactivate); + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + seat_set_exclusive_client(seat, NULL); + struct sway_node *previous = seat_get_focus(seat); + if (previous) { + // Hack to get seat to re-focus the return value of get_focus + seat_set_focus(seat, NULL); + seat_set_focus(seat, previous); } } - - if (inhibit == SHORTCUTS_INHIBIT_DISABLE) { - /** - * Here we deny to honour the inhibitor by never sending the - * activate signal. We can not, however, destroy the inhibitor - * because the protocol doesn't allow for it. So it will linger - * until the client removes it im- or explicitly. But at least - * it can only be one inhibitor per surface and seat at a time. - * - * We also want to allow the user to activate the inhibitor - * manually later which is why we do this check here where the - * inhibitor is already attached to its seat and ready for use. - */ - return; - } - - wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor); } void handle_virtual_keyboard(struct wl_listener *listener, void *data) { struct sway_input_manager *input_manager = wl_container_of(listener, input_manager, virtual_keyboard_new); struct wlr_virtual_keyboard_v1 *keyboard = data; - struct wlr_input_device *device = &keyboard->keyboard.base; + struct wlr_input_device *device = &keyboard->input_device; - // TODO: Amend protocol to allow NULL seat - struct sway_seat *seat = keyboard->seat ? - input_manager_sway_seat_from_wlr_seat(keyboard->seat) : - input_manager_get_default_seat(); + struct sway_seat *seat = input_manager_get_default_seat(); + // TODO: The user might want this on a different seat struct sway_input_device *input_device = calloc(1, sizeof(struct sway_input_device)); if (!sway_assert(input_device, "could not allocate input device")) { @@ -381,7 +551,6 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) { } device->data = input_device; - input_device->is_virtual = true; input_device->wlr_device = device; input_device->identifier = input_device_get_identifier(device); wl_list_insert(&input_manager->devices, &input_device->link); @@ -395,57 +564,6 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) { seat_add_device(seat, input_device); } -void handle_virtual_pointer(struct wl_listener *listener, void *data) { - struct sway_input_manager *input_manager = - wl_container_of(listener, input_manager, virtual_pointer_new); - struct wlr_virtual_pointer_v1_new_pointer_event *event = data; - struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; - struct wlr_input_device *device = &pointer->pointer.base; - - struct sway_seat *seat = event->suggested_seat ? - input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : - input_manager_get_default_seat(); - - struct sway_input_device *input_device = - calloc(1, sizeof(struct sway_input_device)); - if (!sway_assert(input_device, "could not allocate input device")) { - return; - } - device->data = input_device; - - input_device->is_virtual = true; - input_device->wlr_device = device; - input_device->identifier = input_device_get_identifier(device); - wl_list_insert(&input_manager->devices, &input_device->link); - - sway_log(SWAY_DEBUG, "adding virtual pointer: '%s'", - input_device->identifier); - - wl_signal_add(&device->events.destroy, &input_device->device_destroy); - input_device->device_destroy.notify = handle_device_destroy; - - seat_add_device(seat, input_device); - - if (event->suggested_output) { - wlr_cursor_map_input_to_output(seat->cursor->cursor, device, - event->suggested_output); - } -} - -static void handle_transient_seat_manager_create_seat( - struct wl_listener *listener, void *data) { - struct wlr_transient_seat_v1 *transient_seat = data; - static uint64_t i; - char name[256]; - snprintf(name, sizeof(name), "transient-%"PRIx64, i++); - struct sway_seat *seat = seat_create(name); - if (seat && seat->wlr_seat) { - wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat); - } else { - wlr_transient_seat_v1_deny(transient_seat); - } -} - struct sway_input_manager *input_manager_create(struct sway_server *server) { struct sway_input_manager *input = calloc(1, sizeof(struct sway_input_manager)); @@ -465,42 +583,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { &input->virtual_keyboard_new); input->virtual_keyboard_new.notify = handle_virtual_keyboard; - input->virtual_pointer = wlr_virtual_pointer_manager_v1_create( - server->wl_display - ); - wl_signal_add(&input->virtual_pointer->events.new_virtual_pointer, - &input->virtual_pointer_new); - input->virtual_pointer_new.notify = handle_virtual_pointer; - - input->keyboard_shortcuts_inhibit = - wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); - input->keyboard_shortcuts_inhibit_new_inhibitor.notify = - handle_keyboard_shortcuts_inhibit_new_inhibitor; - wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, - &input->keyboard_shortcuts_inhibit_new_inhibitor); - - input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); - - input->transient_seat_manager = - wlr_transient_seat_manager_v1_create(server->wl_display); - assert(input->transient_seat_manager); - - input->transient_seat_create.notify = - handle_transient_seat_manager_create_seat; - wl_signal_add(&input->transient_seat_manager->events.create_seat, - &input->transient_seat_create); + input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); + input->inhibit_activate.notify = handle_inhibit_activate; + wl_signal_add(&input->inhibit->events.activate, + &input->inhibit_activate); + input->inhibit_deactivate.notify = handle_inhibit_deactivate; + wl_signal_add(&input->inhibit->events.deactivate, + &input->inhibit_deactivate); return input; } -void input_manager_finish(struct sway_input_manager *input) { - wl_list_remove(&input->new_input.link); - wl_list_remove(&input->virtual_keyboard_new.link); - wl_list_remove(&input->virtual_pointer_new.link); - wl_list_remove(&input->keyboard_shortcuts_inhibit_new_inhibitor.link); - wl_list_remove(&input->transient_seat_create.link); -} - bool input_manager_has_focus(struct sway_node *node) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { @@ -520,119 +613,53 @@ void input_manager_set_focus(struct sway_node *node) { } } -/** - * 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 || ic->xkb_file) { - // this is the first config with xkb_layout or xkb_file - if (ic->identifier == input_config->identifier) { - translate_keysyms(ic); - } - - return; - } - } - - for (int i = 0; i < config->input_type_configs->length; ++i) { - struct input_config *ic = config->input_type_configs->items[i]; - if (ic->xkb_layout || ic->xkb_file) { - // this is the first config with xkb_layout or xkb_file - if (ic->identifier == input_config->identifier) { - translate_keysyms(ic); - } - - return; - } - } -} - -static void input_manager_configure_input( - struct sway_input_device *input_device) { -#if WLR_HAS_LIBINPUT_BACKEND - bool config_changed = sway_input_configure_libinput_device(input_device); -#else - bool config_changed = false; -#endif - struct sway_seat *seat = NULL; - wl_list_for_each(seat, &server.input->seats, link) { - seat_configure_device(seat, input_device); - } - if (config_changed) { - ipc_event_input("libinput_config", input_device); - } -} - -void input_manager_configure_all_input_mappings(void) { - struct sway_input_device *input_device; - wl_list_for_each(input_device, &server.input->devices, link) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seat_configure_device_mapping(seat, input_device); - } - -#if WLR_HAS_LIBINPUT_BACKEND - // Input devices mapped to unavailable outputs get their libinput - // send_events setting switched to false. We need to re-enable this - // when the output appears. - sway_input_configure_libinput_device_send_events(input_device); -#endif - } -} - 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 = has_prefix(input_config->identifier, "type:"); 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 - || type_matches) { - input_manager_configure_input(input_device); + || wildcard) { + 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_TOUCH) { + input_manager_libinput_config_touch(input_device); + } + + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + seat_configure_device(seat, input_device); + } } } - - retranslate_keysyms(input_config); } void input_manager_reset_input(struct sway_input_device *input_device) { -#if WLR_HAS_LIBINPUT_BACKEND - sway_input_reset_libinput_device(input_device); -#endif + if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || + input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { + 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_TOUCH) { + input_manager_libinput_reset_touch(input_device); + } + struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { seat_reset_device(seat, input_device); } } -void input_manager_reset_all_inputs(void) { - // Set the active keyboard to NULL to avoid spamming configuration updates - // for all keyboard devices. - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - wlr_seat_set_keyboard(seat->wlr_seat, NULL); - } - +void input_manager_reset_all_inputs() { struct sway_input_device *input_device = NULL; wl_list_for_each(input_device, &server.input->devices, link) { input_manager_reset_input(input_device); } - - // If there is at least one keyboard using the default keymap, repeat delay, - // and repeat rate, then it is possible that there is a keyboard group that - // need their keyboard disarmed. - wl_list_for_each(seat, &server.input->seats, link) { - struct sway_keyboard_group *group; - wl_list_for_each(group, &seat->keyboard_groups, link) { - sway_keyboard_disarm_key_repeat(group->seat_device->keyboard); - } - } } + void input_manager_apply_seat_config(struct seat_config *seat_config) { sway_log(SWAY_DEBUG, "applying seat config for seat %s", seat_config->name); if (strcmp(seat_config->name, "*") == 0) { @@ -721,13 +748,5 @@ 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 d18237375..00fc6a13f 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -1,26 +1,19 @@ #include #include #include -#include #include +#include +#include #include -#include -#include -#include #include #include "sway/commands.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" -#include "sway/input/cursor.h" #include "sway/ipc-server.h" -#include "sway/server.h" #include "log.h" -#if WLR_HAS_SESSION -#include -#endif - static struct modifier_key { char *name; uint32_t mod; @@ -34,7 +27,6 @@ static struct modifier_key { { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, { "Mod3", WLR_MODIFIER_MOD3 }, { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, - { "Super", WLR_MODIFIER_LOGO }, { "Mod5", WLR_MODIFIER_MOD5 }, }; @@ -77,9 +69,8 @@ int get_modifier_names(const char **names, uint32_t modifier_masks) { /** * Remove all key ids associated to a keycode from the list of pressed keys */ -static bool state_erase_key(struct sway_shortcut_state *state, +static void state_erase_key(struct sway_shortcut_state *state, uint32_t keycode) { - bool found = false; size_t j = 0; for (size_t i = 0; i < state->npressed; ++i) { if (i > j) { @@ -88,8 +79,6 @@ static bool state_erase_key(struct sway_shortcut_state *state, } if (state->pressed_keycodes[i] != keycode) { ++j; - } else { - found = true; } } while(state->npressed > j) { @@ -98,7 +87,6 @@ static bool state_erase_key(struct sway_shortcut_state *state, state->pressed_keycodes[state->npressed] = 0; } state->current_key = 0; - return found; } /** @@ -129,8 +117,8 @@ static void state_add_key(struct sway_shortcut_state *state, /** * Update the shortcut model state in response to new input */ -static bool update_shortcut_state(struct sway_shortcut_state *state, - uint32_t keycode, enum wl_keyboard_key_state keystate, uint32_t new_key, +static void update_shortcut_state(struct sway_shortcut_state *state, + struct wlr_event_keyboard_key *event, uint32_t new_key, uint32_t raw_modifiers) { bool last_key_was_a_modifier = raw_modifiers != state->last_raw_modifiers; state->last_raw_modifiers = raw_modifiers; @@ -140,15 +128,13 @@ static bool update_shortcut_state(struct sway_shortcut_state *state, state_erase_key(state, state->last_keycode); } - if (keystate == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (event->state == WLR_KEY_PRESSED) { // Add current key to set; there may be duplicates - state_add_key(state, keycode, new_key); - state->last_keycode = keycode; + state_add_key(state, event->keycode, new_key); + state->last_keycode = event->keycode; } else { - return state_erase_key(state, keycode); + state_erase_key(state, event->keycode); } - - return false; } /** @@ -157,22 +143,17 @@ static bool update_shortcut_state(struct sway_shortcut_state *state, */ static void get_active_binding(const struct sway_shortcut_state *state, list_t *bindings, struct sway_binding **current_binding, - uint32_t modifiers, bool release, bool locked, bool inhibited, - const char *input, bool exact_input, xkb_layout_index_t group) { + 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) != 0; - bool binding_inhibited = (binding->flags & BINDING_INHIBITED) != 0; + bool binding_locked = binding->flags & BINDING_LOCKED; bool binding_release = binding->flags & BINDING_RELEASE; if (modifiers ^ binding->modifiers || release != binding_release || locked > binding_locked || - inhibited > binding_inhibited || - (binding->group != XKB_LAYOUT_INVALID && - binding->group != group) || (strcmp(binding->input, input) != 0 && - (strcmp(binding->input, "*") != 0 || exact_input))) { + strcmp(binding->input, "*") != 0)) { continue; } @@ -197,62 +178,18 @@ static void get_active_binding(const struct sway_shortcut_state *state, continue; } - if (*current_binding) { - if (*current_binding == binding) { - 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; } - - bool current_locked = - ((*current_binding)->flags & BINDING_LOCKED) != 0; - bool current_inhibited = - ((*current_binding)->flags & BINDING_INHIBITED) != 0; - bool current_input = strcmp((*current_binding)->input, input) == 0; - bool current_group_set = - (*current_binding)->group != XKB_LAYOUT_INVALID; - bool binding_input = strcmp(binding->input, input) == 0; - bool binding_group_set = binding->group != XKB_LAYOUT_INVALID; - - if (current_input == binding_input - && current_locked == binding_locked - && current_inhibited == binding_inhibited - && current_group_set == binding_group_set) { - 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_binding)->group == group) { - continue; // Prefer correct group for matching inputs - } - - if (current_input == binding_input && - current_group_set == binding_group_set && - current_locked == locked) { - continue; // Prefer correct lock state for matching input+group - } - - if (current_input == binding_input && - current_group_set == binding_group_set && - current_locked == binding_locked && - current_inhibited == inhibited) { - // Prefer correct inhibition state for matching - // input+group+locked - continue; - } - } - - *current_binding = binding; - if (strcmp((*current_binding)->input, input) == 0 && - (((*current_binding)->flags & BINDING_LOCKED) == locked) && - (((*current_binding)->flags & BINDING_INHIBITED) == inhibited) && - (*current_binding)->group == group) { - return; // If a perfect match is found, quit searching } } } @@ -268,45 +205,16 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { for (size_t i = 0; i < keysyms_len; ++i) { xkb_keysym_t keysym = pressed_keysyms[i]; - if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { -#if WLR_HAS_SESSION - if (server.session) { - unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; - wlr_session_change_vt(server.session, vt); + if (wlr_backend_is_multi(server.backend)) { + struct wlr_session *session = + wlr_backend_get_session(server.backend); + if (session) { + unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; + wlr_session_change_vt(session, vt); + } } -#endif - return true; - } - } - - return false; -} - -static bool keyboard_execute_pointer_keysyms(struct sway_keyboard *keyboard, - uint32_t time, const xkb_keysym_t *pressed_keysyms, size_t keysyms_len, - enum wl_keyboard_key_state state) { - struct sway_cursor *cursor = keyboard->seat_device->sway_seat->cursor; - - for (size_t i = 0; i < keysyms_len; ++i) { - xkb_keysym_t keysym = pressed_keysyms[i]; - - uint32_t button = wlr_keyboard_keysym_to_pointer_button(keysym); - if (button != 0) { - dispatch_cursor_button(cursor, &keyboard->wlr->base, time, button, - (enum wl_pointer_button_state)state); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - return true; - } - - int dx, dy; - wlr_keyboard_keysym_to_pointer_motion(keysym, &dx, &dy); - if (state == WL_KEYBOARD_KEY_STATE_PRESSED && (dx != 0 || dy != 0)) { - dx *= 10; - dy *= 10; - pointer_motion(cursor, time, &keyboard->wlr->base, dx, dy, dx, dy); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); return true; } } @@ -326,12 +234,14 @@ static bool keyboard_execute_pointer_keysyms(struct sway_keyboard *keyboard, static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, xkb_keycode_t keycode, const xkb_keysym_t **keysyms, uint32_t *modifiers) { - *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); + struct wlr_input_device *device = + keyboard->seat_device->input_device->wlr_device; + *modifiers = wlr_keyboard_get_modifiers(device->keyboard); xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( - keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); + device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); *modifiers = *modifiers & ~consumed; - return xkb_state_key_get_syms(keyboard->wlr->xkb_state, + return xkb_state_key_get_syms(device->keyboard->xkb_state, keycode, keysyms); } @@ -347,11 +257,13 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, xkb_keycode_t keycode, const xkb_keysym_t **keysyms, uint32_t *modifiers) { - *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); + struct wlr_input_device *device = + keyboard->seat_device->input_device->wlr_device; + *modifiers = wlr_keyboard_get_modifiers(device->keyboard); xkb_layout_index_t layout_index = xkb_state_key_get_layout( - keyboard->wlr->xkb_state, keycode); - return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap, + device->keyboard->xkb_state, keycode); + return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, keycode, layout_index, 0, keysyms); } @@ -365,152 +277,96 @@ void sway_keyboard_disarm_key_repeat(struct sway_keyboard *keyboard) { } } -struct key_info { - uint32_t keycode; - uint32_t code_modifiers; - - const xkb_keysym_t *raw_keysyms; - uint32_t raw_modifiers; - size_t raw_keysyms_len; - - const xkb_keysym_t *translated_keysyms; - uint32_t translated_modifiers; - size_t translated_keysyms_len; -}; - -static void update_keyboard_state(struct sway_keyboard *keyboard, - uint32_t raw_keycode, enum wl_keyboard_key_state keystate, - struct key_info *keyinfo) { - // Identify new keycode, raw keysym(s), and translated keysym(s) - keyinfo->keycode = raw_keycode + 8; - - keyinfo->raw_keysyms_len = keyboard_keysyms_raw(keyboard, keyinfo->keycode, - &keyinfo->raw_keysyms, &keyinfo->raw_modifiers); - - keyinfo->translated_keysyms_len = keyboard_keysyms_translated(keyboard, - keyinfo->keycode, &keyinfo->translated_keysyms, - &keyinfo->translated_modifiers); - - keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); - - // Update shortcut model keyinfo - update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, - keyinfo->keycode, keyinfo->code_modifiers); - for (size_t i = 0; i < keyinfo->raw_keysyms_len; ++i) { - update_shortcut_state(&keyboard->state_keysyms_raw, - raw_keycode, keystate, keyinfo->raw_keysyms[i], - keyinfo->code_modifiers); - } - for (size_t i = 0; i < keyinfo->translated_keysyms_len; ++i) { - update_shortcut_state(&keyboard->state_keysyms_translated, - raw_keycode, keystate, keyinfo->translated_keysyms[i], - keyinfo->code_modifiers); - } -} - -/** - * Get keyboard grab of the seat from sway_keyboard if we should forward events - * to it. - * - * Returns NULL if the keyboard is not grabbed by an input method, - * or if event is from virtual keyboard of the same client as grab. - * TODO: see swaywm/wlroots#2322 - */ -static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab( - struct sway_keyboard *keyboard) { - struct wlr_input_method_v2 *input_method = keyboard->seat_device-> - sway_seat->im_relay.input_method; - struct wlr_virtual_keyboard_v1 *virtual_keyboard = - wlr_input_device_get_virtual_keyboard(keyboard->seat_device->input_device->wlr_device); - if (!input_method || !input_method->keyboard_grab || (virtual_keyboard && - wl_resource_get_client(virtual_keyboard->resource) == - wl_resource_get_client(input_method->keyboard_grab->resource))) { - return NULL; - } - return input_method->keyboard_grab; -} - -static void handle_key_event(struct sway_keyboard *keyboard, - struct wlr_keyboard_key_event *event) { - struct sway_seat *seat = keyboard->seat_device->sway_seat; +static void handle_keyboard_key(struct wl_listener *listener, void *data) { + struct sway_keyboard *keyboard = + wl_container_of(listener, keyboard, keyboard_key); + struct sway_seat* seat = keyboard->seat_device->sway_seat; struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_input_device *wlr_device = keyboard->seat_device->input_device->wlr_device; char *device_identifier = input_device_get_identifier(wlr_device); - bool exact_identifier = keyboard->wlr->group != NULL; - seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); - bool locked = server.session_lock.lock; - struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = - keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); - bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; - - if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - cursor_notify_key_press(seat->cursor); - } + wlr_idle_notify_activity(server.idle, wlr_seat); + struct wlr_event_keyboard_key *event = data; + bool input_inhibited = seat->exclusive_client != NULL; // Identify new keycode, raw keysym(s), and translated keysym(s) - struct key_info keyinfo; - update_keyboard_state(keyboard, event->keycode, event->state, &keyinfo); + xkb_keycode_t keycode = event->keycode + 8; + + 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); + } bool handled = false; + // Identify active release binding struct sway_binding *binding_released = NULL; get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding_released, - keyinfo.code_modifiers, true, locked, - shortcuts_inhibited, device_identifier, - exact_identifier, keyboard->effective_layout); - get_active_binding(&keyboard->state_keysyms_raw, - config->current_mode->keysym_bindings, &binding_released, - keyinfo.raw_modifiers, true, locked, - shortcuts_inhibited, device_identifier, - exact_identifier, keyboard->effective_layout); + code_modifiers, true, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding_released, - keyinfo.translated_modifiers, true, locked, - shortcuts_inhibited, device_identifier, - exact_identifier, keyboard->effective_layout); + 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); // Execute stored release binding once no longer active if (keyboard->held_binding && binding_released != keyboard->held_binding && - event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { + event->state == WLR_KEY_RELEASED) { seat_execute_command(seat, keyboard->held_binding); handled = true; } if (binding_released != keyboard->held_binding) { keyboard->held_binding = NULL; } - if (binding_released && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (binding_released && event->state == WLR_KEY_PRESSED) { keyboard->held_binding = binding_released; } // Identify and execute active pressed binding struct sway_binding *binding = NULL; - if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (event->state == WLR_KEY_PRESSED) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding, - keyinfo.code_modifiers, false, locked, - shortcuts_inhibited, device_identifier, - exact_identifier, keyboard->effective_layout); - get_active_binding(&keyboard->state_keysyms_raw, - config->current_mode->keysym_bindings, &binding, - keyinfo.raw_modifiers, false, locked, - shortcuts_inhibited, device_identifier, - exact_identifier, keyboard->effective_layout); + code_modifiers, false, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding, - keyinfo.translated_modifiers, false, locked, - shortcuts_inhibited, device_identifier, - exact_identifier, keyboard->effective_layout); + 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 // binding may remove the keyboard, the timer needs to be updated first - if (binding && !(binding->flags & BINDING_NOREPEAT) && - keyboard->wlr->repeat_info.delay > 0) { + if (binding && wlr_device->keyboard->repeat_info.delay > 0) { keyboard->repeat_binding = binding; if (wl_event_source_timer_update(keyboard->key_repeat_source, - keyboard->wlr->repeat_info.delay) < 0) { + wlr_device->keyboard->repeat_info.delay) < 0) { sway_log(SWAY_DEBUG, "failed to set key repeat timer"); } } else if (keyboard->repeat_binding) { @@ -522,149 +378,45 @@ static void handle_key_event(struct sway_keyboard *keyboard, handled = true; } - if (!handled && keyboard->wlr->group) { - // Only handle device specific bindings for keyboards in a group - free(device_identifier); - return; - } - // Compositor bindings - if (!handled && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (!handled && event->state == WLR_KEY_PRESSED) { handled = keyboard_execute_compositor_binding( - keyboard, keyinfo.translated_keysyms, - keyinfo.translated_modifiers, keyinfo.translated_keysyms_len); + keyboard, translated_keysyms, translated_modifiers, + translated_keysyms_len); } - if (!handled && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (!handled && event->state == WLR_KEY_PRESSED) { handled = keyboard_execute_compositor_binding( - keyboard, keyinfo.raw_keysyms, keyinfo.raw_modifiers, - keyinfo.raw_keysyms_len); - } - if (!handled) { - handled = keyboard_execute_pointer_keysyms(keyboard, event->time_msec, - keyinfo.translated_keysyms, keyinfo.translated_keysyms_len, - event->state); + keyboard, raw_keysyms, raw_modifiers, + raw_keysyms_len); } - if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { - // If the pressed event was sent to a client and we have a focused - // surface immediately before this event, also send the released - // event. In particular, don't send the released event to the IM grab. - bool pressed_sent = update_shortcut_state( - &keyboard->state_pressed_sent, event->keycode, - event->state, keyinfo.keycode, 0); - if (pressed_sent && seat->wlr_seat->keyboard_state.focused_surface) { - wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); - wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, - event->keycode, event->state); - handled = true; - } - } - - if (!handled) { - struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); - - if (kb_grab) { - wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); - wlr_input_method_keyboard_grab_v2_send_key(kb_grab, - event->time_msec, event->keycode, event->state); - handled = true; - } - } - - if (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) { - // If a released event failed pressed sent test, and not in sent to - // keyboard grab, it is still not handled. Don't handle released here. - update_shortcut_state( - &keyboard->state_pressed_sent, event->keycode, event->state, - keyinfo.keycode, 0); - wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); + if (!handled || event->state == WLR_KEY_RELEASED) { + wlr_seat_set_keyboard(wlr_seat, wlr_device); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); } + transaction_commit_dirty(); + free(device_identifier); } -static void handle_keyboard_key(struct wl_listener *listener, void *data) { - struct sway_keyboard *keyboard = - wl_container_of(listener, keyboard, keyboard_key); - handle_key_event(keyboard, data); -} - -static void handle_keyboard_group_key(struct wl_listener *listener, - void *data) { - struct sway_keyboard_group *sway_group = - wl_container_of(listener, sway_group, keyboard_key); - handle_key_event(sway_group->seat_device->keyboard, data); -} - -static void handle_keyboard_group_enter(struct wl_listener *listener, - void *data) { - struct sway_keyboard_group *sway_group = - wl_container_of(listener, sway_group, enter); - struct sway_keyboard *keyboard = sway_group->seat_device->keyboard; - struct wl_array *keycodes = data; - - uint32_t *keycode; - wl_array_for_each(keycode, keycodes) { - struct key_info keyinfo; - update_keyboard_state(keyboard, *keycode, WL_KEYBOARD_KEY_STATE_PRESSED, &keyinfo); - } -} - -static void handle_keyboard_group_leave(struct wl_listener *listener, - void *data) { - struct sway_keyboard_group *sway_group = - wl_container_of(listener, sway_group, leave); - struct sway_keyboard *keyboard = sway_group->seat_device->keyboard; - struct wl_array *keycodes = data; - - bool pressed_sent = false; - - uint32_t *keycode; - wl_array_for_each(keycode, keycodes) { - struct key_info keyinfo; - update_keyboard_state(keyboard, *keycode, WL_KEYBOARD_KEY_STATE_RELEASED, &keyinfo); - - pressed_sent |= update_shortcut_state(&keyboard->state_pressed_sent, - *keycode, WL_KEYBOARD_KEY_STATE_RELEASED, keyinfo.keycode, 0); - } - - if (!pressed_sent) { - return; - } - - // Refocus the focused node, layer surface, or unmanaged surface so that - // it picks up the current keyboard state. - struct sway_seat *seat = keyboard->seat_device->sway_seat; - struct sway_node *focus = seat_get_focus(seat); - if (focus) { - seat_set_focus(seat, NULL); - seat_set_focus(seat, focus); - } else if (seat->focused_layer) { - struct wlr_layer_surface_v1 *layer = seat->focused_layer; - seat_set_focus_layer(seat, NULL); - seat_set_focus_layer(seat, layer); - } else { - struct wlr_surface *unmanaged = seat->wlr_seat->keyboard_state.focused_surface; - seat_set_focus_surface(seat, NULL, false); - seat_set_focus_surface(seat, unmanaged, false); - } -} - static int handle_keyboard_repeat(void *data) { - struct sway_keyboard *keyboard = data; + struct sway_keyboard *keyboard = (struct sway_keyboard *)data; + struct wlr_keyboard *wlr_device = + keyboard->seat_device->input_device->wlr_device->keyboard; if (keyboard->repeat_binding) { - if (keyboard->wlr->repeat_info.rate > 0) { + if (wlr_device->repeat_info.rate > 0) { // We queue the next event first, as the command might cancel it if (wl_event_source_timer_update(keyboard->key_repeat_source, - 1000 / keyboard->wlr->repeat_info.rate) < 0) { + 1000 / wlr_device->repeat_info.rate) < 0) { sway_log(SWAY_DEBUG, "failed to update key repeat timer"); } } seat_execute_command(keyboard->seat_device->sway_seat, keyboard->repeat_binding); + transaction_commit_dirty(); } return 0; } @@ -672,64 +424,29 @@ 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 (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; + 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; ipc_event_bar_state_update(bar); } } } } -static void handle_modifier_event(struct sway_keyboard *keyboard) { - if (!keyboard->wlr->group) { - struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); - - if (kb_grab) { - wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); - wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, - &keyboard->wlr->modifiers); - } else { - struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; - wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); - wlr_seat_keyboard_notify_modifiers(wlr_seat, - &keyboard->wlr->modifiers); - } - - uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); - determine_bar_visibility(modifiers); - } - - if (keyboard->wlr->modifiers.group != keyboard->effective_layout) { - keyboard->effective_layout = keyboard->wlr->modifiers.group; - - if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) { - ipc_event_input("xkb_layout", keyboard->seat_device->input_device); - } - } -} - static void handle_keyboard_modifiers(struct wl_listener *listener, void *data) { struct sway_keyboard *keyboard = wl_container_of(listener, keyboard, keyboard_modifiers); - handle_modifier_event(keyboard); -} + struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; + struct wlr_input_device *wlr_device = + keyboard->seat_device->input_device->wlr_device; + wlr_seat_set_keyboard(wlr_seat, wlr_device); + wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers); -static void handle_keyboard_group_modifiers(struct wl_listener *listener, - void *data) { - struct sway_keyboard_group *group = - wl_container_of(listener, group, keyboard_modifiers); - handle_modifier_event(group->seat_device->keyboard); + uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); + determine_bar_visibility(modifiers); } struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, @@ -741,7 +458,6 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, } keyboard->seat_device = device; - keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device); device->keyboard = keyboard; wl_list_init(&keyboard->keyboard_key.link); @@ -753,337 +469,86 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, return keyboard; } -static void handle_xkb_context_log(struct xkb_context *context, - enum xkb_log_level level, const char *format, va_list args) { - char *error = vformat_str(format, args); - - size_t len = strlen(error); - if (error[len - 1] == '\n') { - error[len - 1] = '\0'; - } - - sway_log_importance_t importance = SWAY_DEBUG; - if (level <= XKB_LOG_LEVEL_ERROR) { // Critical and Error - importance = SWAY_ERROR; - } else if (level <= XKB_LOG_LEVEL_INFO) { // Warning and Info - importance = SWAY_INFO; - } - sway_log(importance, "[xkbcommon] %s", error); - - char **data = xkb_context_get_user_data(context); - if (importance == SWAY_ERROR && data && !*data) { - *data = error; - } else { - free(error); - } -} - -struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, - char **error) { - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); - if (!sway_assert(context, "cannot create XKB context")) { - return NULL; - } - xkb_context_set_user_data(context, error); - xkb_context_set_log_fn(context, handle_xkb_context_log); - - struct xkb_keymap *keymap = NULL; - - if (ic && ic->xkb_file) { - FILE *keymap_file = fopen(ic->xkb_file, "r"); - if (!keymap_file) { - sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); - if (error) { - *error = format_str("cannot read xkb file %s: %s", - ic->xkb_file, strerror(errno)); - } - goto cleanup; - } - - keymap = xkb_keymap_new_from_file(context, keymap_file, - XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); - - if (fclose(keymap_file) != 0) { - sway_log_errno(SWAY_ERROR, "Failed to close xkb file %s", - ic->xkb_file); - } - } else { - struct xkb_rule_names rules = {0}; - if (ic) { - input_config_fill_rule_names(ic, &rules); - } - keymap = xkb_keymap_new_from_names(context, &rules, - XKB_KEYMAP_COMPILE_NO_FLAGS); - } - -cleanup: - xkb_context_set_user_data(context, NULL); - xkb_context_unref(context); - return keymap; -} - -static bool repeat_info_match(struct sway_keyboard *a, struct wlr_keyboard *b) { - return a->repeat_rate == b->repeat_info.rate && - a->repeat_delay == b->repeat_info.delay; -} - -static void destroy_empty_wlr_keyboard_group(void *data) { - wlr_keyboard_group_destroy(data); -} - -static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { - struct sway_input_device *device = keyboard->seat_device->input_device; - struct wlr_keyboard_group *wlr_group = keyboard->wlr->group; - - sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", - device->identifier, wlr_group); - - wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr); - - if (wl_list_empty(&wlr_group->devices)) { - sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", - wlr_group); - struct sway_keyboard_group *sway_group = wlr_group->data; - wlr_group->data = NULL; - wl_list_remove(&sway_group->link); - wl_list_remove(&sway_group->keyboard_key.link); - wl_list_remove(&sway_group->keyboard_modifiers.link); - wl_list_remove(&sway_group->enter.link); - wl_list_remove(&sway_group->leave.link); - sway_keyboard_destroy(sway_group->seat_device->keyboard); - free(sway_group->seat_device->input_device); - free(sway_group->seat_device); - free(sway_group); - - // To prevent use-after-free conditions when handling key events, defer - // freeing the wlr_keyboard_group until idle - wl_event_loop_add_idle(server.wl_event_loop, - destroy_empty_wlr_keyboard_group, wlr_group); - } -} - -static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { - if (!keyboard->wlr->group) { - return; - } - - struct sway_seat *seat = keyboard->seat_device->sway_seat; - struct seat_config *sc = seat_get_config(seat); - if (!sc) { - sc = seat_get_config_by_name("*"); - } - - switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) { - case KEYBOARD_GROUP_NONE: - sway_keyboard_group_remove(keyboard); - break; - case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ - case KEYBOARD_GROUP_SMART:; - struct wlr_keyboard_group *group = keyboard->wlr->group; - if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || - !repeat_info_match(keyboard, &group->keyboard)) { - sway_keyboard_group_remove(keyboard); - } - break; - } -} - -static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { - struct sway_input_device *device = keyboard->seat_device->input_device; - struct sway_seat *seat = keyboard->seat_device->sway_seat; - struct seat_config *sc = seat_get_config(seat); - - if (device->is_virtual) { - // Virtual devices should not be grouped - return; - } - - if (!sc) { - sc = seat_get_config_by_name("*"); - } - - if (sc && sc->keyboard_grouping == KEYBOARD_GROUP_NONE) { - // Keyboard grouping is disabled for the seat - return; - } - - struct sway_keyboard_group *group; - wl_list_for_each(group, &seat->keyboard_groups, link) { - switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) { - case KEYBOARD_GROUP_NONE: - // Nothing to do. This shouldn't even be reached - return; - case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ - case KEYBOARD_GROUP_SMART:; - struct wlr_keyboard_group *wlr_group = group->wlr_group; - if (wlr_keyboard_keymaps_match(keyboard->keymap, - wlr_group->keyboard.keymap) && - repeat_info_match(keyboard, &wlr_group->keyboard)) { - sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", - device->identifier, wlr_group); - wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr); - return; - } - break; - } - } - - struct sway_keyboard_group *sway_group = - calloc(1, sizeof(struct sway_keyboard_group)); - if (!sway_group) { - sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard_group"); - return; - } - - sway_group->wlr_group = wlr_keyboard_group_create(); - if (!sway_group->wlr_group) { - sway_log(SWAY_ERROR, "Failed to create keyboard group"); - goto cleanup; - } - sway_group->wlr_group->data = sway_group; - wlr_keyboard_set_keymap(&sway_group->wlr_group->keyboard, keyboard->keymap); - wlr_keyboard_set_repeat_info(&sway_group->wlr_group->keyboard, - keyboard->repeat_rate, keyboard->repeat_delay); - sway_log(SWAY_DEBUG, "Created keyboard group %p", sway_group->wlr_group); - - sway_group->seat_device = calloc(1, sizeof(struct sway_seat_device)); - if (!sway_group->seat_device) { - sway_log(SWAY_ERROR, "Failed to allocate sway_seat_device for group"); - goto cleanup; - } - sway_group->seat_device->sway_seat = seat; - - sway_group->seat_device->input_device = - calloc(1, sizeof(struct sway_input_device)); - if (!sway_group->seat_device->input_device) { - sway_log(SWAY_ERROR, "Failed to allocate sway_input_device for group"); - goto cleanup; - } - sway_group->seat_device->input_device->wlr_device = - &sway_group->wlr_group->keyboard.base; - - if (!sway_keyboard_create(seat, sway_group->seat_device)) { - sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); - goto cleanup; - } - - sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", - device->identifier, sway_group->wlr_group); - wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr); - - wl_list_insert(&seat->keyboard_groups, &sway_group->link); - - wl_signal_add(&sway_group->wlr_group->keyboard.events.key, - &sway_group->keyboard_key); - sway_group->keyboard_key.notify = handle_keyboard_group_key; - - wl_signal_add(&sway_group->wlr_group->keyboard.events.modifiers, - &sway_group->keyboard_modifiers); - sway_group->keyboard_modifiers.notify = handle_keyboard_group_modifiers; - - wl_signal_add(&sway_group->wlr_group->events.enter, &sway_group->enter); - sway_group->enter.notify = handle_keyboard_group_enter; - - wl_signal_add(&sway_group->wlr_group->events.leave, &sway_group->leave); - sway_group->leave.notify = handle_keyboard_group_leave; - return; - -cleanup: - if (sway_group && sway_group->wlr_group) { - wlr_keyboard_group_destroy(sway_group->wlr_group); - } - free(sway_group->seat_device->keyboard); - free(sway_group->seat_device->input_device); - free(sway_group->seat_device); - free(sway_group); -} - -static void sway_keyboard_set_layout(struct sway_keyboard *keyboard, - struct input_config *input_config) { - struct xkb_keymap *keymap = sway_keyboard_compile_keymap(input_config, NULL); - if (!keymap) { - sway_log(SWAY_ERROR, "Failed to compile keymap. Attempting defaults"); - keymap = sway_keyboard_compile_keymap(NULL, NULL); - if (!keymap) { - sway_log(SWAY_ERROR, - "Failed to compile default keymap. Aborting configure"); - return; - } - } - - bool keymap_changed = keyboard->keymap ? - !wlr_keyboard_keymaps_match(keyboard->keymap, keymap) : true; - bool effective_layout_changed = keyboard->effective_layout != 0; - - if (keymap_changed || config->reloading) { - xkb_keymap_unref(keyboard->keymap); - keyboard->keymap = keymap; - keyboard->effective_layout = 0; - - sway_keyboard_group_remove_invalid(keyboard); - wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); - if (!keyboard->wlr->group) { - sway_keyboard_group_add(keyboard); - } - - xkb_mod_mask_t locked_mods = 0; - if (input_config && input_config->xkb_numlock > 0) { - xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, - XKB_MOD_NAME_NUM); - if (mod_index != XKB_MOD_INVALID) { - locked_mods |= (uint32_t)1 << mod_index; - } - } - if (input_config && input_config->xkb_capslock > 0) { - xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, - XKB_MOD_NAME_CAPS); - if (mod_index != XKB_MOD_INVALID) { - locked_mods |= (uint32_t)1 << mod_index; - } - } - if (locked_mods) { - wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0, - locked_mods, 0); - uint32_t leds = 0; - for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { - if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state, - keyboard->wlr->led_indexes[i])) { - leds |= (1 << i); - } - } - if (keyboard->wlr->group) { - wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds); - } else { - wlr_keyboard_led_update(keyboard->wlr, leds); - } - } - } else { - xkb_keymap_unref(keymap); - sway_keyboard_group_remove_invalid(keyboard); - if (!keyboard->wlr->group) { - sway_keyboard_group_add(keyboard); - } - } - - if (keymap_changed) { - ipc_event_input("xkb_keymap", - keyboard->seat_device->input_device); - } else if (effective_layout_changed) { - ipc_event_input("xkb_layout", - keyboard->seat_device->input_device); - } -} - 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 (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), - "sway_keyboard_configure should not be called with a " - "keyboard group's keyboard")) { + 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); + + if (!keymap) { + sway_log(SWAY_DEBUG, "cannot configure keyboard: keymap does not exist"); + xkb_context_unref(context); + return; + } + + xkb_keymap_unref(keyboard->keymap); + keyboard->keymap = keymap; + wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); + + xkb_mod_mask_t locked_mods = 0; + if (input_config && input_config->xkb_numlock > 0) { + xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_NUM); + if (mod_index != XKB_MOD_INVALID) { + locked_mods |= (uint32_t)1 << mod_index; + } + } + if (input_config && input_config->xkb_capslock > 0) { + xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS); + if (mod_index != XKB_MOD_INVALID) { + locked_mods |= (uint32_t)1 << mod_index; + } + } + if (locked_mods) { + wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, locked_mods, 0); + uint32_t leds = 0; + for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { + if (xkb_state_led_index_is_active(wlr_device->keyboard->xkb_state, + wlr_device->keyboard->led_indexes[i])) { + leds |= (1 << i); + } + } + wlr_keyboard_led_update(wlr_device->keyboard, leds); + } + int repeat_rate = 25; if (input_config && input_config->repeat_rate != INT_MIN) { repeat_rate = input_config->repeat_rate; @@ -1092,51 +557,27 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { if (input_config && input_config->repeat_delay != INT_MIN) { repeat_delay = input_config->repeat_delay; } + wlr_keyboard_set_repeat_info(wlr_device->keyboard, repeat_rate, + repeat_delay); - bool repeat_info_changed = keyboard->repeat_rate != repeat_rate || - keyboard->repeat_delay != repeat_delay; - - if (repeat_info_changed || config->reloading) { - keyboard->repeat_rate = repeat_rate; - keyboard->repeat_delay = repeat_delay; - - wlr_keyboard_set_repeat_info(keyboard->wlr, - keyboard->repeat_rate, keyboard->repeat_delay); - } - - if (!keyboard->seat_device->input_device->is_virtual) { - sway_keyboard_set_layout(keyboard, input_config); - } - - // If the seat has no active keyboard, set this one + xkb_context_unref(context); struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; - struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; - if (current_keyboard == NULL) { - wlr_seat_set_keyboard(seat, keyboard->wlr); - } + wlr_seat_set_keyboard(seat, wlr_device); wl_list_remove(&keyboard->keyboard_key.link); - wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); + wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); keyboard->keyboard_key.notify = handle_keyboard_key; wl_list_remove(&keyboard->keyboard_modifiers.link); - wl_signal_add(&keyboard->wlr->events.modifiers, + wl_signal_add(&wlr_device->keyboard->events.modifiers, &keyboard->keyboard_modifiers); keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; - } void sway_keyboard_destroy(struct sway_keyboard *keyboard) { if (!keyboard) { return; } - if (keyboard->wlr->group) { - sway_keyboard_group_remove(keyboard); - } - struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; - if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) { - wlr_seat_set_keyboard(wlr_seat, NULL); - } if (keyboard->keymap) { xkb_keymap_unref(keyboard->keymap); } diff --git a/sway/input/libinput.c b/sway/input/libinput.c deleted file mode 100644 index fd7533d99..000000000 --- a/sway/input/libinput.c +++ /dev/null @@ -1,423 +0,0 @@ -#include -#include -#include -#include -#include -#include "log.h" -#include "sway/config.h" -#include "sway/output.h" -#include "sway/input/input-manager.h" -#include "sway/ipc-server.h" - -static void log_status(enum libinput_config_status status) { - if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) { - sway_log(SWAY_ERROR, "Failed to apply libinput config: %s", - libinput_config_status_to_str(status)); - } -} - -static bool set_send_events(struct libinput_device *device, uint32_t mode) { - if (libinput_device_config_send_events_get_mode(device) == mode) { - return false; - } - sway_log(SWAY_DEBUG, "send_events_set_mode(%" PRIu32 ")", mode); - log_status(libinput_device_config_send_events_set_mode(device, mode)); - return true; -} - -static bool set_tap(struct libinput_device *device, - enum libinput_config_tap_state tap) { - if (libinput_device_config_tap_get_finger_count(device) <= 0 || - libinput_device_config_tap_get_enabled(device) == tap) { - return false; - } - sway_log(SWAY_DEBUG, "tap_set_enabled(%d)", tap); - log_status(libinput_device_config_tap_set_enabled(device, tap)); - return true; -} - -static bool set_tap_button_map(struct libinput_device *device, - enum libinput_config_tap_button_map map) { - if (libinput_device_config_tap_get_finger_count(device) <= 0 || - libinput_device_config_tap_get_button_map(device) == map) { - return false; - } - sway_log(SWAY_DEBUG, "tap_set_button_map(%d)", map); - log_status(libinput_device_config_tap_set_button_map(device, map)); - return true; -} - -static bool set_tap_drag(struct libinput_device *device, - enum libinput_config_drag_state drag) { - if (libinput_device_config_tap_get_finger_count(device) <= 0 || - libinput_device_config_tap_get_drag_enabled(device) == drag) { - return false; - } - sway_log(SWAY_DEBUG, "tap_set_drag_enabled(%d)", drag); - log_status(libinput_device_config_tap_set_drag_enabled(device, drag)); - return true; -} - -static bool set_tap_drag_lock(struct libinput_device *device, - enum libinput_config_drag_lock_state lock) { - if (libinput_device_config_tap_get_finger_count(device) <= 0 || - libinput_device_config_tap_get_drag_lock_enabled(device) == lock) { - return false; - } - sway_log(SWAY_DEBUG, "tap_set_drag_lock_enabled(%d)", lock); - log_status(libinput_device_config_tap_set_drag_lock_enabled(device, lock)); - return true; -} - -static bool set_accel_speed(struct libinput_device *device, double speed) { - if (!libinput_device_config_accel_is_available(device) || - libinput_device_config_accel_get_speed(device) == speed) { - return false; - } - sway_log(SWAY_DEBUG, "accel_set_speed(%f)", speed); - log_status(libinput_device_config_accel_set_speed(device, speed)); - return true; -} - -static bool set_rotation_angle(struct libinput_device *device, double angle) { - if (!libinput_device_config_rotation_is_available(device) || - libinput_device_config_rotation_get_angle(device) == angle) { - return false; - } - sway_log(SWAY_DEBUG, "rotation_set_angle(%f)", angle); - log_status(libinput_device_config_rotation_set_angle(device, angle)); - return true; -} - -static bool set_accel_profile(struct libinput_device *device, - enum libinput_config_accel_profile profile) { - if (!libinput_device_config_accel_is_available(device) || - libinput_device_config_accel_get_profile(device) == profile) { - return false; - } - sway_log(SWAY_DEBUG, "accel_set_profile(%d)", profile); - log_status(libinput_device_config_accel_set_profile(device, profile)); - return true; -} - -static bool set_natural_scroll(struct libinput_device *d, bool n) { - if (!libinput_device_config_scroll_has_natural_scroll(d) || - libinput_device_config_scroll_get_natural_scroll_enabled(d) == n) { - return false; - } - sway_log(SWAY_DEBUG, "scroll_set_natural_scroll(%d)", n); - log_status(libinput_device_config_scroll_set_natural_scroll_enabled(d, n)); - return true; -} - -static bool set_left_handed(struct libinput_device *device, bool left) { - if (!libinput_device_config_left_handed_is_available(device) || - libinput_device_config_left_handed_get(device) == left) { - return false; - } - sway_log(SWAY_DEBUG, "left_handed_set(%d)", left); - log_status(libinput_device_config_left_handed_set(device, left)); - return true; -} - -static bool set_click_method(struct libinput_device *device, - enum libinput_config_click_method method) { - uint32_t click = libinput_device_config_click_get_methods(device); - if ((click & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) == 0 || - libinput_device_config_click_get_method(device) == method) { - return false; - } - sway_log(SWAY_DEBUG, "click_set_method(%d)", method); - log_status(libinput_device_config_click_set_method(device, method)); - return true; -} - -static bool set_clickfinger_button_map(struct libinput_device *device, - enum libinput_config_clickfinger_button_map map) { - if (libinput_device_config_click_get_clickfinger_button_map(device) == map) { - return false; - } - sway_log(SWAY_DEBUG, "clickfinger_set_button_map(%d)", map); - log_status(libinput_device_config_click_set_clickfinger_button_map(device, map)); - return true; -} - -static bool set_middle_emulation(struct libinput_device *dev, - enum libinput_config_middle_emulation_state mid) { - if (!libinput_device_config_middle_emulation_is_available(dev) || - libinput_device_config_middle_emulation_get_enabled(dev) == mid) { - return false; - } - sway_log(SWAY_DEBUG, "middle_emulation_set_enabled(%d)", mid); - log_status(libinput_device_config_middle_emulation_set_enabled(dev, mid)); - return true; -} - -static bool set_scroll_method(struct libinput_device *device, - enum libinput_config_scroll_method method) { - uint32_t scroll = libinput_device_config_scroll_get_methods(device); - if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || - libinput_device_config_scroll_get_method(device) == method) { - return false; - } - sway_log(SWAY_DEBUG, "scroll_set_method(%d)", method); - log_status(libinput_device_config_scroll_set_method(device, method)); - return true; -} - -static bool set_scroll_button(struct libinput_device *dev, uint32_t button) { - uint32_t scroll = libinput_device_config_scroll_get_methods(dev); - if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || - libinput_device_config_scroll_get_button(dev) == button) { - return false; - } - sway_log(SWAY_DEBUG, "scroll_set_button(%" PRIu32 ")", button); - log_status(libinput_device_config_scroll_set_button(dev, button)); - return true; -} - -static bool set_scroll_button_lock(struct libinput_device *dev, - enum libinput_config_scroll_button_lock_state lock) { - uint32_t scroll = libinput_device_config_scroll_get_methods(dev); - if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || - libinput_device_config_scroll_get_button_lock(dev) == lock) { - return false; - } - sway_log(SWAY_DEBUG, "scroll_set_button_lock(%" PRIu32 ")", lock); - log_status(libinput_device_config_scroll_set_button_lock(dev, lock)); - return true; -} - -static bool set_dwt(struct libinput_device *device, bool dwt) { - if (!libinput_device_config_dwt_is_available(device) || - libinput_device_config_dwt_get_enabled(device) == dwt) { - return false; - } - sway_log(SWAY_DEBUG, "dwt_set_enabled(%d)", dwt); - log_status(libinput_device_config_dwt_set_enabled(device, dwt)); - return true; -} - -static bool set_dwtp(struct libinput_device *device, bool dwtp) { - if (!libinput_device_config_dwtp_is_available(device) || - libinput_device_config_dwtp_get_enabled(device) == dwtp) { - return false; - } - sway_log(SWAY_DEBUG, "dwtp_set_enabled(%d)", dwtp); - log_status(libinput_device_config_dwtp_set_enabled(device, dwtp)); - return true; -} - -static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { - if (!libinput_device_config_calibration_has_matrix(dev)) { - return false; - } - bool changed = false; - float current[6]; - libinput_device_config_calibration_get_matrix(dev, current); - for (int i = 0; i < 6; i++) { - if (current[i] != mat[i]) { - changed = true; - break; - } - } - if (changed) { - sway_log(SWAY_DEBUG, "calibration_set_matrix(%f, %f, %f, %f, %f, %f)", - mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - log_status(libinput_device_config_calibration_set_matrix(dev, mat)); - } - return changed; -} - -static bool configure_send_events(struct libinput_device *device, - struct input_config *ic) { - if (ic->mapped_to_output && - strcmp("*", ic->mapped_to_output) != 0 && - !output_by_name_or_id(ic->mapped_to_output)) { - sway_log(SWAY_DEBUG, - "%s '%s' is mapped to offline output '%s'; disabling input", - ic->input_type, ic->identifier, ic->mapped_to_output); - return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); - } else if (ic->send_events != INT_MIN) { - return set_send_events(device, ic->send_events); - } else { - // Have to reset to the default mode here, otherwise if ic->send_events - // is unset and a mapped output just came online after being disabled, - // we'd remain stuck sending no events. - return set_send_events(device, - libinput_device_config_send_events_get_default_mode(device)); - } -} - -bool sway_input_configure_libinput_device(struct sway_input_device *input_device) { - struct input_config *ic = input_device_get_config(input_device); - if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { - return false; - } - - struct libinput_device *device = - wlr_libinput_get_device_handle(input_device->wlr_device); - sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", - ic->identifier, input_device->identifier); - - bool changed = configure_send_events(device, ic); - if (ic->tap != INT_MIN) { - changed |= set_tap(device, ic->tap); - } - if (ic->tap_button_map != INT_MIN) { - changed |= set_tap_button_map(device, ic->tap_button_map); - } - if (ic->drag != INT_MIN) { - changed |= set_tap_drag(device, ic->drag); - } - if (ic->drag_lock != INT_MIN) { - changed |= set_tap_drag_lock(device, ic->drag_lock); - } - if (ic->pointer_accel != FLT_MIN) { - changed |= set_accel_speed(device, ic->pointer_accel); - } - if (ic->rotation_angle != FLT_MIN) { - changed |= set_rotation_angle(device, ic->rotation_angle); - } - if (ic->accel_profile != INT_MIN) { - changed |= set_accel_profile(device, ic->accel_profile); - } - if (ic->natural_scroll != INT_MIN) { - changed |= set_natural_scroll(device, ic->natural_scroll); - } - if (ic->left_handed != INT_MIN) { - changed |= set_left_handed(device, ic->left_handed); - } - if (ic->click_method != INT_MIN) { - changed |= set_click_method(device, ic->click_method); - } - if (ic->clickfinger_button_map != INT_MIN) { - changed |= set_clickfinger_button_map(device, ic->clickfinger_button_map); - } - if (ic->middle_emulation != INT_MIN) { - changed |= set_middle_emulation(device, ic->middle_emulation); - } - if (ic->scroll_method != INT_MIN) { - changed |= set_scroll_method(device, ic->scroll_method); - } - if (ic->scroll_button != INT_MIN) { - changed |= set_scroll_button(device, ic->scroll_button); - } - if (ic->scroll_button_lock != INT_MIN) { - changed |= set_scroll_button_lock(device, ic->scroll_button_lock); - } - if (ic->dwt != INT_MIN) { - changed |= set_dwt(device, ic->dwt); - } - if (ic->dwtp != INT_MIN) { - changed |= set_dwtp(device, ic->dwtp); - } - if (ic->calibration_matrix.configured) { - changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); - } - - return changed; -} - -void sway_input_configure_libinput_device_send_events( - struct sway_input_device *input_device) { - struct input_config *ic = input_device_get_config(input_device); - if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { - return; - } - - struct libinput_device *device = - wlr_libinput_get_device_handle(input_device->wlr_device); - bool changed = configure_send_events(device, ic); - - if (changed) { - ipc_event_input("libinput_config", input_device); - } -} - -void sway_input_reset_libinput_device(struct sway_input_device *input_device) { - if (!wlr_input_device_is_libinput(input_device->wlr_device)) { - return; - } - - struct libinput_device *device = - wlr_libinput_get_device_handle(input_device->wlr_device); - sway_log(SWAY_DEBUG, "sway_input_reset_libinput_device(%s)", - input_device->identifier); - bool changed = false; - - changed |= set_send_events(device, - libinput_device_config_send_events_get_default_mode(device)); - changed |= set_tap(device, - libinput_device_config_tap_get_default_enabled(device)); - changed |= set_tap_button_map(device, - libinput_device_config_tap_get_default_button_map(device)); - changed |= set_tap_drag(device, - libinput_device_config_tap_get_default_drag_enabled(device)); - changed |= set_tap_drag_lock(device, - libinput_device_config_tap_get_default_drag_lock_enabled(device)); - changed |= set_accel_speed(device, - libinput_device_config_accel_get_default_speed(device)); - changed |= set_rotation_angle(device, - libinput_device_config_rotation_get_default_angle(device)); - changed |= set_accel_profile(device, - libinput_device_config_accel_get_default_profile(device)); - changed |= set_natural_scroll(device, - libinput_device_config_scroll_get_default_natural_scroll_enabled( - device)); - changed |= set_left_handed(device, - libinput_device_config_left_handed_get_default(device)); - changed |= set_click_method(device, - libinput_device_config_click_get_default_method(device)); - changed |= set_clickfinger_button_map(device, - libinput_device_config_click_get_default_clickfinger_button_map(device)); - changed |= set_middle_emulation(device, - libinput_device_config_middle_emulation_get_default_enabled(device)); - changed |= set_scroll_method(device, - libinput_device_config_scroll_get_default_method(device)); - changed |= set_scroll_button(device, - libinput_device_config_scroll_get_default_button(device)); - changed |= set_dwt(device, - libinput_device_config_dwt_get_default_enabled(device)); - changed |= set_dwtp(device, - libinput_device_config_dwtp_get_default_enabled(device)); - - float matrix[6]; - libinput_device_config_calibration_get_default_matrix(device, matrix); - changed |= set_calibration_matrix(device, matrix); - - if (changed) { - ipc_event_input("libinput_config", input_device); - } -} - -static bool sway_udev_device_is_builtin(struct udev_device *udev_device) { - const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH"); - if (!id_path) { - return false; - } - - if (has_prefix(id_path, "platform-")) { - return true; - } - - return has_prefix(id_path, "pci-") && strstr(id_path, "-platform-"); -} - -bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { - if (!wlr_input_device_is_libinput(sway_device->wlr_device)) { - return false; - } - - struct libinput_device *device = - wlr_libinput_get_device_handle(sway_device->wlr_device); - struct udev_device *udev_device = - libinput_device_get_udev_device(device); - if (!udev_device) { - return false; - } - - bool is_builtin = sway_udev_device_is_builtin(udev_device); - udev_device_unref(udev_device); - return is_builtin; -} diff --git a/sway/input/seat.c b/sway/input/seat.c index ebdbd91ed..df48b7518 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,35 +1,24 @@ -#include +#define _POSIX_C_SOURCE 200809L #include #include #include #include -#include #include #include -#include -#include -#include #include #include -#include -#include #include #include "config.h" -#include "list.h" #include "log.h" -#include "sway/config.h" -#include "sway/scene_descriptor.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/libinput.h" #include "sway/input/seat.h" -#include "sway/input/switch.h" -#include "sway/input/tablet.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" -#include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -42,74 +31,32 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { } sway_keyboard_destroy(seat_device->keyboard); - sway_tablet_destroy(seat_device->tablet); - sway_tablet_pad_destroy(seat_device->tablet_pad); - sway_switch_destroy(seat_device->switch_device); wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, seat_device->input_device->wlr_device); wl_list_remove(&seat_device->link); free(seat_device); } -static void seat_node_destroy(struct sway_seat_node *seat_node) { - wl_list_remove(&seat_node->destroy.link); - wl_list_remove(&seat_node->link); - - /* - * This is the only time we remove items from the focus stack without - * immediately re-adding them. If we just removed the last thing, - * mark that nothing has focus anymore. - */ - if (wl_list_empty(&seat_node->seat->focus_stack)) { - seat_node->seat->has_focus = false; - } - - free(seat_node); -} - void seat_destroy(struct sway_seat *seat) { - wlr_seat_destroy(seat->wlr_seat); -} - -static void handle_seat_destroy(struct wl_listener *listener, void *data) { - struct sway_seat *seat = wl_container_of(listener, seat, destroy); - - if (seat == config->handler_context.seat) { - config->handler_context.seat = input_manager_get_default_seat(); - } struct sway_seat_device *seat_device, *next; wl_list_for_each_safe(seat_device, next, &seat->devices, link) { seat_device_destroy(seat_device); } - struct sway_seat_node *seat_node, *next_seat_node; - wl_list_for_each_safe(seat_node, next_seat_node, &seat->focus_stack, - link) { - seat_node_destroy(seat_node); - } - sway_input_method_relay_finish(&seat->im_relay); sway_cursor_destroy(seat->cursor); wl_list_remove(&seat->new_node.link); - wl_list_remove(&seat->request_start_drag.link); - wl_list_remove(&seat->start_drag.link); + wl_list_remove(&seat->new_drag_icon.link); wl_list_remove(&seat->request_set_selection.link); wl_list_remove(&seat->request_set_primary_selection.link); wl_list_remove(&seat->link); - wl_list_remove(&seat->destroy.link); - for (int i = 0; i < seat->deferred_bindings->length; i++) { - free_sway_binding(seat->deferred_bindings->items[i]); - } - wlr_scene_node_destroy(&seat->scene_tree->node); - list_free(seat->deferred_bindings); + wlr_seat_destroy(seat->wlr_seat); free(seat->prev_workspace_name); free(seat); } -void seat_idle_notify_activity(struct sway_seat *seat, - enum sway_input_idle_source source) { - if ((source & seat->idle_inhibit_sources) == 0) { - return; - } - wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); +static void seat_node_destroy(struct sway_seat_node *seat_node) { + wl_list_remove(&seat_node->destroy.link); + wl_list_remove(&seat_node->link); + free(seat_node); } /** @@ -131,54 +78,6 @@ static void seat_send_activate(struct sway_node *node, struct sway_seat *seat) { } } -static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( - struct sway_seat *seat, struct wlr_keyboard *wlr_keyboard) { - struct sway_seat_device *seat_device; - wl_list_for_each(seat_device, &seat->devices, link) { - struct sway_input_device *input_device = seat_device->input_device; - if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { - continue; - } - if (input_device->wlr_device == &wlr_keyboard->base) { - return seat_device->keyboard; - } - } - struct sway_keyboard_group *group; - wl_list_for_each(group, &seat->keyboard_groups, link) { - struct sway_input_device *input_device = - group->seat_device->input_device; - if (input_device->wlr_device == &wlr_keyboard->base) { - return group->seat_device->keyboard; - } - } - return NULL; -} - -static void seat_keyboard_notify_enter(struct sway_seat *seat, - struct wlr_surface *surface) { - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - if (!keyboard) { - wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL); - return; - } - - struct sway_keyboard *sway_keyboard = - sway_keyboard_for_wlr_keyboard(seat, keyboard); - assert(sway_keyboard && "Cannot find sway_keyboard for seat keyboard"); - - struct sway_shortcut_state *state = &sway_keyboard->state_pressed_sent; - wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, - state->pressed_keycodes, state->npressed, &keyboard->modifiers); -} - -static void seat_tablet_pads_set_focus(struct sway_seat *seat, - struct wlr_surface *surface) { - struct sway_seat_device *seat_device; - wl_list_for_each(seat_device, &seat->devices, link) { - sway_tablet_pad_set_focus(seat_device->tablet_pad, surface); - } -} - /** * If con is a view, set it as active and enable keyboard input. * If con is a container, set all child views as active and don't enable @@ -191,16 +90,21 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { node->sway_container->view : NULL; if (view && seat_is_input_allowed(seat, view->surface)) { -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); } #endif - - seat_keyboard_notify_enter(seat, view->surface); - seat_tablet_pads_set_focus(seat, view->surface); - sway_input_method_relay_set_focus(&seat->im_relay, view->surface); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + if (keyboard) { + wlr_seat_keyboard_notify_enter(seat->wlr_seat, + view->surface, keyboard->keycodes, + keyboard->num_keycodes, &keyboard->modifiers); + } else { + wlr_seat_keyboard_notify_enter( + seat->wlr_seat, view->surface, NULL, 0, NULL); + } struct wlr_pointer_constraint_v1 *constraint = wlr_pointer_constraints_v1_constraint_for_surface( @@ -219,13 +123,14 @@ void seat_for_each_node(struct sway_seat *seat, struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_node *ancestor) { - if (node_is_view(ancestor)) { + if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { return ancestor->sway_container; } struct sway_seat_node *current; wl_list_for_each(current, &seat->focus_stack, link) { struct sway_node *node = current->node; - if (node_is_view(node) && node_has_ancestor(node, ancestor)) { + if (node->type == N_CONTAINER && node->sway_container->view && + node_has_ancestor(node, ancestor)) { return node->sway_container; } } @@ -242,19 +147,6 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { if (node->type == N_WORKSPACE) { seat_node_destroy(seat_node); - // If an unmanaged or layer surface is focused when an output gets - // disabled and an empty workspace on the output was focused by the - // seat, the seat needs to refocus its focus inactive to update the - // value of seat->workspace. - if (seat->workspace == node->sway_workspace) { - struct sway_node *node = seat_get_focus_inactive(seat, &root->node); - seat_set_focus(seat, NULL); - if (node) { - seat_set_focus(seat, node); - } else { - seat->workspace = NULL; - } - } return; } @@ -266,14 +158,14 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { seat_node_destroy(seat_node); - if (!parent && !needs_new_focus) { + if (!parent) { // 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 && parent != NULL) { + while (next_focus == NULL) { struct sway_container *con = seat_get_focus_inactive_view(seat, parent); next_focus = con ? &con->node : NULL; @@ -286,16 +178,6 @@ 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 @@ -303,23 +185,16 @@ 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 - if (seat_get_focus(seat) == next_focus) { - seat_send_focus(next_focus, seat); - } else { - seat_set_focus(seat, next_focus); - } + seat_send_focus(next_focus, seat); + seat_set_focus(seat, next_focus); } else { // Setting focus_inactive focus = seat_get_focus_inactive(seat, &root->node); seat_set_raw_focus(seat, next_focus); - if (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) { - seat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node); + if (focus->type == N_CONTAINER) { + seat_set_raw_focus(seat, &focus->sway_container->workspace->node); } seat_set_raw_focus(seat, focus); } @@ -360,118 +235,88 @@ static void handle_new_node(struct wl_listener *listener, void *data) { seat_node_from_node(seat, node); } -static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) { - struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON); - struct wlr_cursor *cursor = seat->cursor->cursor; - - switch (wlr_icon->drag->grab_type) { - case WLR_DRAG_GRAB_KEYBOARD: +static void drag_icon_damage_whole(struct sway_drag_icon *icon) { + if (!icon->wlr_drag_icon->mapped) { return; - case WLR_DRAG_GRAB_KEYBOARD_POINTER: - wlr_scene_node_set_position(node, cursor->x, cursor->y); - break; - case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; + } + desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); +} + +void drag_icon_update_position(struct sway_drag_icon *icon) { + drag_icon_damage_whole(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) { + icon->x = cursor->x; + icon->y = cursor->y; + } else { struct wlr_touch_point *point = - wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->drag->touch_id); + wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->touch_id); if (point == NULL) { return; } - wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y); - } -} - -void drag_icons_update_position(struct sway_seat *seat) { - struct wlr_scene_node *node; - wl_list_for_each(node, &seat->drag_icons->children, link) { - drag_icon_update_position(seat, node); - } -} - -static void drag_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_drag *drag = wl_container_of(listener, drag, destroy); - - // Focus enter isn't sent during drag, so refocus the focused node, layer - // surface or unmanaged surface. - struct sway_seat *seat = drag->seat; - struct sway_node *focus = seat_get_focus(seat); - if (focus) { - seat_set_focus(seat, NULL); - seat_set_focus(seat, focus); - } else if (seat->focused_layer) { - struct wlr_layer_surface_v1 *layer = seat->focused_layer; - seat_set_focus_layer(seat, NULL); - seat_set_focus_layer(seat, layer); - } else { - struct wlr_surface *unmanaged = seat->wlr_seat->keyboard_state.focused_surface; - seat_set_focus_surface(seat, NULL, false); - seat_set_focus_surface(seat, unmanaged, false); + icon->x = seat->touch_x; + icon->y = seat->touch_y; } - drag->wlr_drag->data = NULL; - wl_list_remove(&drag->destroy.link); - free(drag); + drag_icon_damage_whole(icon); } -static void handle_request_start_drag(struct wl_listener *listener, +static void drag_icon_handle_surface_commit(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); + struct sway_drag_icon *icon = + wl_container_of(listener, icon, surface_commit); + drag_icon_update_position(icon); } -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; +static void drag_icon_handle_map(struct wl_listener *listener, void *data) { + struct sway_drag_icon *icon = wl_container_of(listener, icon, map); + drag_icon_damage_whole(icon); +} - struct sway_drag *drag = calloc(1, sizeof(struct sway_drag)); - if (drag == NULL) { +static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { + struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); + drag_icon_damage_whole(icon); +} + +static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); + icon->wlr_drag_icon->data = NULL; + wl_list_remove(&icon->link); + wl_list_remove(&icon->surface_commit.link); + wl_list_remove(&icon->unmap.link); + wl_list_remove(&icon->destroy.link); + 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; + + struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); + if (icon == NULL) { sway_log(SWAY_ERROR, "Allocation failed"); return; } - drag->seat = seat; - drag->wlr_drag = wlr_drag; - wlr_drag->data = drag; + icon->seat = seat; + icon->wlr_drag_icon = wlr_drag_icon; + wlr_drag_icon->data = icon; - drag->destroy.notify = drag_handle_destroy; - wl_signal_add(&wlr_drag->events.destroy, &drag->destroy); + icon->surface_commit.notify = drag_icon_handle_surface_commit; + wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); + icon->unmap.notify = drag_icon_handle_unmap; + wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); + icon->map.notify = drag_icon_handle_map; + wl_signal_add(&wlr_drag_icon->events.map, &icon->map); + icon->destroy.notify = drag_icon_handle_destroy; + wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); - struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; - if (wlr_drag_icon != NULL) { - struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon); - if (!tree) { - sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); - return; - } + wl_list_insert(&root->drag_icons, &icon->link); - if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON, - wlr_drag_icon)) { - sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor"); - wlr_scene_node_destroy(&tree->node); - return; - } - - drag_icon_update_position(seat, &tree->node); - } - seatop_begin_default(seat); + drag_icon_update_position(icon); + seatop_abort(seat); } static void handle_request_set_selection(struct wl_listener *listener, @@ -516,18 +361,8 @@ struct sway_seat *seat_create(const char *seat_name) { return NULL; } - bool failed = false; - seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed); - seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed); - if (failed) { - wlr_scene_node_destroy(&seat->scene_tree->node); - free(seat); - return NULL; - } - seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { - wlr_scene_node_destroy(&seat->scene_tree->node); free(seat); return NULL; } @@ -535,42 +370,30 @@ struct sway_seat *seat_create(const char *seat_name) { seat->cursor = sway_cursor_create(seat); if (!seat->cursor) { - wlr_scene_node_destroy(&seat->scene_tree->node); wlr_seat_destroy(seat->wlr_seat); free(seat); return NULL; } - seat->destroy.notify = handle_seat_destroy; - wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy); - - seat->idle_inhibit_sources = seat->idle_wake_sources = - IDLE_SOURCE_KEYBOARD | - IDLE_SOURCE_POINTER | - IDLE_SOURCE_TOUCH | - IDLE_SOURCE_TABLET_PAD | - IDLE_SOURCE_TABLET_TOOL | - IDLE_SOURCE_SWITCH; - // init the focus stack wl_list_init(&seat->focus_stack); - wl_list_init(&seat->devices); - root_for_each_workspace(collect_focus_workspace_iter, seat); root_for_each_container(collect_focus_container_iter, seat); - seat->deferred_bindings = create_list(); + if (!wl_list_empty(&server.input->seats)) { + // Since this is not the first seat, attempt to set initial focus + struct sway_seat *current_seat = input_manager_current_seat(); + struct sway_node *current_focus = + seat_get_focus_inactive(current_seat, &root->node); + seat_set_focus(seat, current_focus); + } wl_signal_add(&root->events.new_node, &seat->new_node); seat->new_node.notify = handle_new_node; - 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.new_drag_icon, &seat->new_drag_icon); + seat->new_drag_icon.notify = handle_new_drag_icon; wl_signal_add(&seat->wlr_seat->events.request_set_selection, &seat->request_set_selection); @@ -581,30 +404,15 @@ struct sway_seat *seat_create(const char *seat_name) { seat->request_set_primary_selection.notify = handle_request_set_primary_selection; - wl_list_init(&seat->keyboard_groups); - wl_list_init(&seat->keyboard_shortcuts_inhibitors); + wl_list_init(&seat->devices); - sway_input_method_relay_init(seat, &seat->im_relay); - - bool first = wl_list_empty(&server.input->seats); wl_list_insert(&server.input->seats, &seat->link); - if (!first) { - // Since this is not the first seat, attempt to set initial focus - struct sway_seat *current_seat = input_manager_current_seat(); - struct sway_node *current_focus = - seat_get_focus_inactive(current_seat, &root->node); - seat_set_focus(seat, current_focus); - } - - seatop_begin_default(seat); - return seat; } static void seat_update_capabilities(struct sway_seat *seat) { uint32_t caps = 0; - uint32_t previous_caps = seat->wlr_seat->capabilities; struct sway_seat_device *seat_device; wl_list_for_each(seat_device, &seat->devices, link) { switch (seat_device->input_device->wlr_device->type) { @@ -617,11 +425,11 @@ static void seat_update_capabilities(struct sway_seat *seat) { case WLR_INPUT_DEVICE_TOUCH: caps |= WL_SEAT_CAPABILITY_TOUCH; break; - case WLR_INPUT_DEVICE_TABLET: + case WLR_INPUT_DEVICE_TABLET_TOOL: caps |= WL_SEAT_CAPABILITY_POINTER; break; - case WLR_INPUT_DEVICE_SWITCH: case WLR_INPUT_DEVICE_TABLET_PAD: + case WLR_INPUT_DEVICE_SWITCH: break; } } @@ -634,9 +442,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { wlr_seat_set_capabilities(seat->wlr_seat, caps); } else { wlr_seat_set_capabilities(seat->wlr_seat, caps); - if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { - cursor_set_image(seat->cursor, "default", NULL); - } + cursor_set_image(seat->cursor, "left_ptr", NULL); } } @@ -648,129 +454,37 @@ static void seat_reset_input_config(struct sway_seat *seat, sway_device->input_device->wlr_device, NULL); } -/** - * Get the name of the built-in output, if any. Returns NULL if there isn't - * exactly one built-in output. - */ -static const char *get_builtin_output_name(void) { - const char *match = NULL; - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - const char *name = output->wlr_output->name; - if (has_prefix(name, "eDP-") || has_prefix(name, "LVDS-") || - has_prefix(name, "DSI-")) { - if (match != NULL) { - return NULL; - } - match = name; - } - } - return match; -} - -static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { - switch (seat_device->input_device->wlr_device->type) { - case WLR_INPUT_DEVICE_TOUCH: - case WLR_INPUT_DEVICE_TABLET: - return true; - default: - return false; - } -} - -static void seat_apply_input_mapping(struct sway_seat *seat, +static void seat_apply_input_config(struct sway_seat *seat, struct sway_seat_device *sway_device) { - struct input_config *ic = - input_device_get_config(sway_device->input_device); + const char *mapped_to_output = NULL; - switch (sway_device->input_device->wlr_device->type) { - case WLR_INPUT_DEVICE_POINTER: - case WLR_INPUT_DEVICE_TOUCH: - case WLR_INPUT_DEVICE_TABLET: - break; - default: - return; // these devices don't support mappings + struct input_config *ic = input_device_get_config( + sway_device->input_device); + if (ic != NULL) { + sway_log(SWAY_DEBUG, "Applying input config to %s", + sway_device->input_device->identifier); + + mapped_to_output = ic->mapped_to_output; } - sway_log(SWAY_DEBUG, "Applying input mapping to %s", - sway_device->input_device->identifier); - - const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; - struct wlr_box *mapped_to_region = ic == NULL ? NULL : ic->mapped_to_region; - enum input_config_mapped_to mapped_to = - ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; - - switch (mapped_to) { - case MAPPED_TO_DEFAULT:; - /* - * If the wlroots backend provides an output name, use that. - * - * Otherwise, try to map built-in touch and pointer devices to the - * built-in output. - */ - struct wlr_input_device *dev = sway_device->input_device->wlr_device; - switch (dev->type) { - case WLR_INPUT_DEVICE_POINTER: - mapped_to_output = wlr_pointer_from_input_device(dev)->output_name; - break; - case WLR_INPUT_DEVICE_TOUCH: - mapped_to_output = wlr_touch_from_input_device(dev)->output_name; - break; - default: - mapped_to_output = NULL; - break; - } -#if WLR_HAS_LIBINPUT_BACKEND - if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && - sway_libinput_device_is_builtin(sway_device->input_device)) { - mapped_to_output = get_builtin_output_name(); - if (mapped_to_output) { - sway_log(SWAY_DEBUG, "Auto-detected output '%s' for device '%s'", - mapped_to_output, sway_device->input_device->identifier); - } - } -#else - (void)is_touch_or_tablet_tool; - (void)get_builtin_output_name; -#endif - if (mapped_to_output == NULL) { - return; - } - /* fallthrough */ - case MAPPED_TO_OUTPUT: + if (mapped_to_output == NULL) { + mapped_to_output = sway_device->input_device->wlr_device->output_name; + } + if (mapped_to_output != NULL) { sway_log(SWAY_DEBUG, "Mapping input device %s to output %s", sway_device->input_device->identifier, mapped_to_output); if (strcmp("*", mapped_to_output) == 0) { wlr_cursor_map_input_to_output(seat->cursor->cursor, sway_device->input_device->wlr_device, NULL); - wlr_cursor_map_input_to_region(seat->cursor->cursor, - sway_device->input_device->wlr_device, NULL); sway_log(SWAY_DEBUG, "Reset output mapping"); return; } struct sway_output *output = output_by_name_or_id(mapped_to_output); - if (!output) { - sway_log(SWAY_DEBUG, "Requested output %s for device %s isn't present", - mapped_to_output, sway_device->input_device->identifier); - return; + if (output) { + wlr_cursor_map_input_to_output(seat->cursor->cursor, + sway_device->input_device->wlr_device, output->wlr_output); + sway_log(SWAY_DEBUG, "Mapped to output %s", output->wlr_output->name); } - wlr_cursor_map_input_to_output(seat->cursor->cursor, - sway_device->input_device->wlr_device, output->wlr_output); - wlr_cursor_map_input_to_region(seat->cursor->cursor, - sway_device->input_device->wlr_device, NULL); - sway_log(SWAY_DEBUG, - "Mapped to output %s", output->wlr_output->name); - return; - case MAPPED_TO_REGION: - sway_log(SWAY_DEBUG, "Mapping input device %s to %d,%d %dx%d", - sway_device->input_device->identifier, - mapped_to_region->x, mapped_to_region->y, - mapped_to_region->width, mapped_to_region->height); - wlr_cursor_map_input_to_output(seat->cursor->cursor, - sway_device->input_device->wlr_device, NULL); - wlr_cursor_map_input_to_region(seat->cursor->cursor, - sway_device->input_device->wlr_device, mapped_to_region); - return; } } @@ -779,8 +493,7 @@ static void seat_configure_pointer(struct sway_seat *seat, seat_configure_xcursor(seat); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); - wl_event_source_timer_update( - seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); + seat_apply_input_config(seat, sway_device); } static void seat_configure_keyboard(struct sway_seat *seat, @@ -788,55 +501,34 @@ static void seat_configure_keyboard(struct sway_seat *seat, if (!seat_device->keyboard) { sway_keyboard_create(seat, seat_device); } - sway_keyboard_configure(seat_device->keyboard); - - // We only need to update the current keyboard, as the rest will be updated - // as they are activated. struct wlr_keyboard *wlr_keyboard = - wlr_keyboard_from_input_device(seat_device->input_device->wlr_device); - struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard; - if (wlr_keyboard != current_keyboard) { - return; + seat_device->input_device->wlr_device->keyboard; + sway_keyboard_configure(seat_device->keyboard); + wlr_seat_set_keyboard(seat->wlr_seat, + seat_device->input_device->wlr_device); + struct sway_node *focus = seat_get_focus(seat); + if (focus && node_is_view(focus)) { + // force notify reenter to pick up the new configuration + wlr_seat_keyboard_clear_focus(seat->wlr_seat); + wlr_seat_keyboard_notify_enter(seat->wlr_seat, + focus->sway_container->view->surface, wlr_keyboard->keycodes, + wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); } - - // Notify reenter to pick up the new configuration. This reuses - // the current focused surface to avoid breaking input grabs. - struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; - if (surface) { - seat_keyboard_notify_enter(seat, surface); - } -} - -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); - } - 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, sway_device->input_device->wlr_device); + seat_apply_input_config(seat, sway_device); } static void seat_configure_tablet_tool(struct sway_seat *seat, struct sway_seat_device *sway_device) { - if (!sway_device->tablet) { - sway_device->tablet = sway_tablet_create(seat, sway_device); - } - sway_configure_tablet(sway_device->tablet); + seat_configure_xcursor(seat); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); -} - -static void seat_configure_tablet_pad(struct sway_seat *seat, - struct sway_seat_device *sway_device) { - if (!sway_device->tablet_pad) { - sway_device->tablet_pad = sway_tablet_pad_create(seat, sway_device); - } - sway_configure_tablet_pad(sway_device->tablet_pad); + seat_apply_input_config(seat, sway_device); } static struct sway_seat_device *seat_get_device(struct sway_seat *seat, @@ -848,13 +540,6 @@ static struct sway_seat_device *seat_get_device(struct sway_seat *seat, } } - struct sway_keyboard_group *group = NULL; - wl_list_for_each(group, &seat->keyboard_groups, link) { - if (group->seat_device->input_device == input_device) { - return group->seat_device; - } - } - return NULL; } @@ -872,31 +557,19 @@ 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; - case WLR_INPUT_DEVICE_TABLET: + case WLR_INPUT_DEVICE_TABLET_TOOL: seat_configure_tablet_tool(seat, seat_device); break; case WLR_INPUT_DEVICE_TABLET_PAD: - seat_configure_tablet_pad(seat, seat_device); + sway_log(SWAY_DEBUG, "TODO: configure tablet pad"); + break; + case WLR_INPUT_DEVICE_SWITCH: + sway_log(SWAY_DEBUG, "TODO: configure switch device"); break; } - - seat_apply_input_mapping(seat, seat_device); -} - -void seat_configure_device_mapping(struct sway_seat *seat, - struct sway_input_device *input_device) { - struct sway_seat_device *seat_device = seat_get_device(seat, input_device); - if (!seat_device) { - return; - } - - seat_apply_input_mapping(seat, seat_device); } void seat_reset_device(struct sway_seat *seat, @@ -917,7 +590,7 @@ void seat_reset_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_TOUCH: seat_reset_input_config(seat, seat_device); break; - case WLR_INPUT_DEVICE_TABLET: + case WLR_INPUT_DEVICE_TABLET_TOOL: seat_reset_input_config(seat, seat_device); break; case WLR_INPUT_DEVICE_TABLET_PAD: @@ -971,103 +644,42 @@ void seat_remove_device(struct sway_seat *seat, seat_update_capabilities(seat); } -static bool xcursor_manager_is_named(const struct wlr_xcursor_manager *manager, - const char *name) { - return (!manager->name && !name) || - (name && manager->name && strcmp(name, manager->name) == 0); -} - void seat_configure_xcursor(struct sway_seat *seat) { - unsigned cursor_size = 24; + // TODO configure theme and size const char *cursor_theme = NULL; - const struct seat_config *seat_config = seat_get_config(seat); - if (!seat_config) { - seat_config = seat_get_config_by_name("*"); - } - if (seat_config) { - cursor_size = seat_config->xcursor_theme.size; - cursor_theme = seat_config->xcursor_theme.name; - } - - if (seat == input_manager_get_default_seat()) { - char cursor_size_fmt[16]; - snprintf(cursor_size_fmt, sizeof(cursor_size_fmt), "%u", cursor_size); - setenv("XCURSOR_SIZE", cursor_size_fmt, 1); - if (cursor_theme != NULL) { - setenv("XCURSOR_THEME", cursor_theme, 1); - } - -#if WLR_HAS_XWAYLAND - if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || - !xcursor_manager_is_named(server.xwayland.xcursor_manager, - cursor_theme) || - server.xwayland.xcursor_manager->size != cursor_size)) { - - wlr_xcursor_manager_destroy(server.xwayland.xcursor_manager); - - server.xwayland.xcursor_manager = - wlr_xcursor_manager_create(cursor_theme, cursor_size); - sway_assert(server.xwayland.xcursor_manager, - "Cannot create XCursor manager for theme"); - - wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); - struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( - server.xwayland.xcursor_manager, "default", 1); - if (xcursor != NULL) { - struct wlr_xcursor_image *image = xcursor->images[0]; - struct wlr_buffer *buffer = wlr_xcursor_image_get_buffer(image); - wlr_xwayland_set_cursor( - server.xwayland.wlr_xwayland, buffer, - image->hotspot_x, image->hotspot_y); - } - } -#endif - } - - /* Create xcursor manager if we don't have one already, or if the - * theme has changed */ - if (!seat->cursor->xcursor_manager || - !xcursor_manager_is_named( - seat->cursor->xcursor_manager, cursor_theme) || - seat->cursor->xcursor_manager->size != cursor_size) { - - wlr_xcursor_manager_destroy(seat->cursor->xcursor_manager); + if (!seat->cursor->xcursor_manager) { seat->cursor->xcursor_manager = - wlr_xcursor_manager_create(cursor_theme, cursor_size); - if (!seat->cursor->xcursor_manager) { - sway_log(SWAY_ERROR, - "Cannot create XCursor manager for theme '%s'", cursor_theme); + wlr_xcursor_manager_create(cursor_theme, 24); + if (sway_assert(seat->cursor->xcursor_manager, + "Cannot create XCursor manager for theme %s", + cursor_theme)) { + return; } - - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *sway_output = root->outputs->items[i]; - struct wlr_output *output = sway_output->wlr_output; - bool result = - wlr_xcursor_manager_load(seat->cursor->xcursor_manager, - output->scale); - if (!result) { - sway_log(SWAY_ERROR, - "Cannot load xcursor theme for output '%s' with scale %f", - output->name, output->scale); - } - } - - // Reset the cursor so that we apply it to outputs that just appeared - cursor_set_image(seat->cursor, NULL, NULL); - cursor_set_image(seat->cursor, "default", NULL); - wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, - seat->cursor->cursor->y); } + + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *sway_output = root->outputs->items[i]; + struct wlr_output *output = sway_output->wlr_output; + bool result = + wlr_xcursor_manager_load(seat->cursor->xcursor_manager, + output->scale); + + sway_assert(!result, + "Cannot load xcursor theme for output '%s' with scale %f", + // TODO: Fractional scaling + output->name, (double)output->scale); + } + + cursor_set_image(seat->cursor, "left_ptr", NULL); + wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, + seat->cursor->cursor->y); } bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface) { - if (server.session_lock.lock) { - return sway_session_lock_has_surface(server.session_lock.lock, surface); - } - return true; + struct wl_client *client = wl_resource_get_client(surface->resource); + return !seat->exclusive_client || seat->exclusive_client == client; } static void send_unfocus(struct sway_container *con, void *data) { @@ -1079,7 +691,7 @@ static void send_unfocus(struct sway_container *con, void *data) { // Unfocus the container and any children (eg. when leaving `focus parent`) static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { sway_cursor_constrain(seat->cursor, NULL); - wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); + wlr_seat_keyboard_clear_focus(seat->wlr_seat); if (node->type == N_WORKSPACE) { workspace_for_each_container(node->sway_workspace, send_unfocus, seat); } else { @@ -1091,7 +703,6 @@ static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { static int handle_urgent_timeout(void *data) { struct sway_view *view = data; view_set_urgent(view, false); - container_update_itself_and_parents(view->container); return 0; } @@ -1100,15 +711,6 @@ 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 = strdup(seat->workspace->name); - if (!seat->prev_workspace_name) { - sway_log(SWAY_ERROR, "Unable to allocate previous workspace name"); - } - } - ipc_event_workspace(seat->workspace, new_ws, "focus"); seat->workspace = new_ws; } @@ -1118,16 +720,18 @@ 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); - - // 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); - } + node_set_dirty(node_get_parent(node)); } -static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) { +void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { + if (seat->focused_layer) { + struct wlr_layer_surface_v1 *layer = seat->focused_layer; + seat_set_focus_layer(seat, NULL); + seat_set_focus(seat, node); + seat_set_focus_layer(seat, layer); + return; + } + struct sway_node *last_focus = seat_get_focus(seat); if (last_focus == node) { return; @@ -1141,28 +745,38 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n view_close_popups(last_focus->sway_container->view); } seat_send_unfocus(last_focus, seat); - sway_input_method_relay_set_focus(&seat->im_relay, NULL); seat->has_focus = false; + update_debug_tree(); return; } struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? - node->sway_workspace : node->sway_container->pending.workspace; + node->sway_workspace : node->sway_container->workspace; struct sway_container *container = node->type == N_CONTAINER ? node->sway_container : NULL; - // Deny setting focus to a view which is hidden by a fullscreen container or global - if (container && container_obstructing_fullscreen_container(container)) { - return; + // Deny setting focus to a view which is hidden by a fullscreen container + if (new_workspace && new_workspace->fullscreen && container && + !container_is_fullscreen_or_child(container)) { + // Unless it's a transient container + if (!container_is_transient_for(container, new_workspace->fullscreen)) { + return; + } } - // Deny setting focus to a workspace node when using fullscreen global if (root->fullscreen_global && !container && new_workspace) { return; } + // Deny setting focus to a view which is hidden by a fullscreen global + if (root->fullscreen_global && container != root->fullscreen_global && + !container_has_ancestor(container, root->fullscreen_global)) { + // Unless it's a transient container + if (!container_is_transient_for(container, root->fullscreen_global)) { + return; + } + } - struct sway_output *new_output = - new_workspace ? new_workspace->output : NULL; + struct sway_output *new_output = new_workspace->output; if (last_workspace != new_workspace && new_output) { node_set_dirty(&new_output->node); @@ -1185,10 +799,10 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n // Put the container parents on the focus stack, then the workspace, then // the focused container. if (container) { - struct sway_container *parent = container->pending.parent; + struct sway_container *parent = container->parent; while (parent) { seat_set_raw_focus(seat, &parent->node); - parent = parent->pending.parent; + parent = parent->parent; } } if (new_workspace) { @@ -1205,22 +819,12 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n ipc_event_window(container, "focus"); } - if (last_workspace && last_workspace != new_workspace) { - wlr_ext_workspace_handle_v1_set_active(last_workspace->ext_workspace, - workspace_is_visible(last_workspace)); - } - if (new_workspace) { - wlr_ext_workspace_handle_v1_set_active(new_workspace->ext_workspace, - workspace_is_visible(new_workspace)); - } - // Move sticky containers to new workspace - if (new_workspace && new_output_last_ws - && new_workspace != new_output_last_ws) { + if (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]; - if (container_is_sticky(floater)) { + if (floater->is_sticky) { container_detach(floater); workspace_add_floating(new_workspace, floater); --i; @@ -1262,29 +866,13 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n seat->has_focus = true; - if (config->smart_gaps && new_workspace) { + if (config->smart_gaps) { // When smart gaps is on, gaps may change when the focus changes so // the workspace needs to be arranged arrange_workspace(new_workspace); } -} -void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { - // Prevents the layer from losing focus if it has keyboard exclusivity - if (seat->has_exclusive_layer) { - struct wlr_layer_surface_v1 *layer = seat->focused_layer; - seat_set_focus_layer(seat, NULL); - seat_set_workspace_focus(seat, node); - seat_set_focus_layer(seat, layer); - } else if (seat->focused_layer) { - seat_set_focus_layer(seat, NULL); - seat_set_workspace_focus(seat, node); - } else { - seat_set_workspace_focus(seat, node); - } - if (server.session_lock.lock) { - seat_set_focus_surface(seat, server.session_lock.lock->focused, false); - } + update_debug_tree(); } void seat_set_focus_container(struct sway_seat *seat, @@ -1304,15 +892,13 @@ void seat_set_focus_surface(struct sway_seat *seat, seat_send_unfocus(focus, seat); seat->has_focus = false; } - - if (surface) { - seat_keyboard_notify_enter(seat, surface); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + if (keyboard) { + wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, + keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } else { - wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); + wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL); } - - sway_input_method_relay_set_focus(&seat->im_relay, surface); - seat_tablet_pads_set_focus(seat, surface); } void seat_set_focus_layer(struct sway_seat *seat, @@ -1326,23 +912,27 @@ void seat_set_focus_layer(struct sway_seat *seat, seat_set_focus(seat, previous); } return; - } else if (!layer) { - return; - } - assert(layer->surface->mapped); - if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && - layer->current.keyboard_interactive - == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { - seat->has_exclusive_layer = true; - } - if (seat->focused_layer == layer) { + } else if (!layer || seat->focused_layer == layer) { return; } seat_set_focus_surface(seat, layer->surface, true); - seat->focused_layer = layer; + if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { + seat->focused_layer = layer; + } } -void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) { +void seat_set_exclusive_client(struct sway_seat *seat, + struct wl_client *client) { + if (!client) { + seat->exclusive_client = client; + // Triggers a refocus of the topmost surface layer if necessary + // TODO: Make layer surface focus per-output based on cursor position + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + arrange_layers(output); + } + return; + } if (seat->focused_layer) { if (wl_resource_get_client(seat->focused_layer->resource) != client) { seat_set_focus_layer(seat, NULL); @@ -1357,7 +947,7 @@ void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client } if (seat->wlr_seat->pointer_state.focused_client) { if (seat->wlr_seat->pointer_state.focused_client->client != client) { - wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); + wlr_seat_pointer_clear_focus(seat->wlr_seat); } } struct timespec now; @@ -1369,6 +959,7 @@ void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client now.tv_nsec / 1000, point->touch_id); } } + seat->exclusive_client = client; } struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, @@ -1398,7 +989,7 @@ struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, struct sway_node *node = current->node; if (node->type == N_CONTAINER && !container_is_floating_or_child(node->sway_container) && - node->sway_container->pending.workspace == workspace) { + node->sway_container->workspace == workspace) { return node->sway_container; } } @@ -1415,7 +1006,7 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, struct sway_node *node = current->node; if (node->type == N_CONTAINER && container_is_floating_or_child(node->sway_container) && - node->sway_container->pending.workspace == workspace) { + node->sway_container->workspace == workspace) { return node->sway_container; } } @@ -1449,39 +1040,26 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; } - sway_assert(!wl_list_empty(&seat->focus_stack), - "focus_stack is empty, but has_focus is true"); + if (wl_list_empty(&seat->focus_stack)) { + return NULL; + } struct sway_seat_node *current = wl_container_of(seat->focus_stack.next, current, link); return current->node; } struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { - struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + struct sway_node *focus = seat_get_focus(seat); if (!focus) { return NULL; } if (focus->type == N_CONTAINER) { - return focus->sway_container->pending.workspace; + return focus->sway_container->workspace; } if (focus->type == N_WORKSPACE) { return focus->sway_workspace; } - 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->pending.workspace) { - return node->sway_container->pending.workspace; - } else if (node->type == N_WORKSPACE) { - return node->sway_workspace; - } - } - return NULL; + return NULL; // unreachable } struct sway_container *seat_get_focused_container(struct sway_seat *seat) { @@ -1500,14 +1078,11 @@ void seat_apply_config(struct sway_seat *seat, return; } - seat->idle_inhibit_sources = seat_config->idle_inhibit_sources; - seat->idle_wake_sources = seat_config->idle_wake_sources; - wl_list_for_each(seat_device, &seat->devices, link) { seat_configure_device(seat, seat_device->input_device); - cursor_handle_activity_from_device(seat->cursor, - seat_device->input_device->wlr_device); } + + cursor_handle_activity(seat->cursor); } struct seat_config *seat_get_config(struct sway_seat *seat) { @@ -1535,7 +1110,8 @@ 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 wl_pointer_button_state state) { + 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); } @@ -1547,185 +1123,62 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } if (config->mouse_warping == WARP_OUTPUT) { struct sway_output *output = node_get_output(focus); - 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; - } + 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 (focus->type == N_CONTAINER) { - cursor_warp_to_container(seat->cursor, focus->sway_container, false); + cursor_warp_to_container(seat->cursor, focus->sway_container); } else { cursor_warp_to_workspace(seat->cursor, focus->sway_workspace); } + if (seat->cursor->hidden){ + cursor_unhide(seat->cursor); + wl_event_source_timer_update(seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); + } +} + +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->unref) { + if (seat->seatop_impl && seat->seatop_impl->unref) { seat->seatop_impl->unref(seat, con); } } -void seatop_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state) { - if (seat->seatop_impl->button) { - seat->seatop_impl->button(seat, time_msec, device, button, state); +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_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { - if (seat->seatop_impl->pointer_motion) { - seat->seatop_impl->pointer_motion(seat, time_msec); - } -} - -void seatop_pointer_axis(struct sway_seat *seat, - struct wlr_pointer_axis_event *event) { - if (seat->seatop_impl->pointer_axis) { - seat->seatop_impl->pointer_axis(seat, event); - } -} - -void seatop_touch_motion(struct sway_seat *seat, struct wlr_touch_motion_event *event, - double lx, double ly) { - if (seat->seatop_impl->touch_motion) { - seat->seatop_impl->touch_motion(seat, event, lx, ly); - } -} - -void seatop_touch_up(struct sway_seat *seat, struct wlr_touch_up_event *event) { - if (seat->seatop_impl->touch_up) { - seat->seatop_impl->touch_up(seat, event); - } -} - -void seatop_touch_down(struct sway_seat *seat, struct wlr_touch_down_event *event, - double lx, double ly) { - if (seat->seatop_impl->touch_down) { - seat->seatop_impl->touch_down(seat, event, lx, ly); - } -} - -void seatop_touch_cancel(struct sway_seat *seat, struct wlr_touch_cancel_event *event) { - if (seat->seatop_impl->touch_cancel) { - seat->seatop_impl->touch_cancel(seat, event); - } -} - -void seatop_tablet_tool_tip(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec, - enum wlr_tablet_tool_tip_state state) { - if (seat->seatop_impl->tablet_tool_tip) { - seat->seatop_impl->tablet_tool_tip(seat, tool, time_msec, state); - } -} - -void seatop_tablet_tool_motion(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec) { - if (seat->seatop_impl->tablet_tool_motion) { - seat->seatop_impl->tablet_tool_motion(seat, tool, time_msec); - } else { - seatop_pointer_motion(seat, time_msec); - } -} - -void seatop_hold_begin(struct sway_seat *seat, - struct wlr_pointer_hold_begin_event *event) { - if (seat->seatop_impl->hold_begin) { - seat->seatop_impl->hold_begin(seat, event); - } -} - -void seatop_hold_end(struct sway_seat *seat, - struct wlr_pointer_hold_end_event *event) { - if (seat->seatop_impl->hold_end) { - seat->seatop_impl->hold_end(seat, event); - } -} - -void seatop_pinch_begin(struct sway_seat *seat, - struct wlr_pointer_pinch_begin_event *event) { - if (seat->seatop_impl->pinch_begin) { - seat->seatop_impl->pinch_begin(seat, event); - } -} - -void seatop_pinch_update(struct sway_seat *seat, - struct wlr_pointer_pinch_update_event *event) { - if (seat->seatop_impl->pinch_update) { - seat->seatop_impl->pinch_update(seat, event); - } -} - -void seatop_pinch_end(struct sway_seat *seat, - struct wlr_pointer_pinch_end_event *event) { - if (seat->seatop_impl->pinch_end) { - seat->seatop_impl->pinch_end(seat, event); - } -} - -void seatop_swipe_begin(struct sway_seat *seat, - struct wlr_pointer_swipe_begin_event *event) { - if (seat->seatop_impl->swipe_begin) { - seat->seatop_impl->swipe_begin(seat, event); - } -} - -void seatop_swipe_update(struct sway_seat *seat, - struct wlr_pointer_swipe_update_event *event) { - if (seat->seatop_impl->swipe_update) { - seat->seatop_impl->swipe_update(seat, event); - } -} - -void seatop_swipe_end(struct sway_seat *seat, - struct wlr_pointer_swipe_end_event *event) { - if (seat->seatop_impl->swipe_end) { - seat->seatop_impl->swipe_end(seat, event); - } -} - -void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { - if (seat->seatop_impl->rebase) { - seat->seatop_impl->rebase(seat, time_msec); - } -} - -void seatop_end(struct sway_seat *seat) { - if (seat->seatop_impl && seat->seatop_impl->end) { - seat->seatop_impl->end(seat); +void seatop_finish(struct sway_seat *seat) { + if (seat->seatop_impl && seat->seatop_impl->finish) { + seat->seatop_impl->finish(seat); } free(seat->seatop_data); seat->seatop_data = NULL; seat->seatop_impl = NULL; } -bool seatop_allows_set_cursor(struct sway_seat *seat) { - return seat->seatop_impl->allow_set_cursor; -} - -struct sway_keyboard_shortcuts_inhibitor * -keyboard_shortcuts_inhibitor_get_for_surface( - const struct sway_seat *seat, - const struct wlr_surface *surface) { - struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = NULL; - wl_list_for_each(sway_inhibitor, &seat->keyboard_shortcuts_inhibitors, link) { - if (sway_inhibitor->inhibitor->surface == surface) { - return sway_inhibitor; - } +void seatop_abort(struct sway_seat *seat) { + if (seat->seatop_impl && seat->seatop_impl->abort) { + seat->seatop_impl->abort(seat); } - - return NULL; + free(seat->seatop_data); + seat->seatop_data = NULL; + seat->seatop_impl = NULL; } -struct sway_keyboard_shortcuts_inhibitor * -keyboard_shortcuts_inhibitor_get_for_focused_surface( - const struct sway_seat *seat) { - return keyboard_shortcuts_inhibitor_get_for_surface(seat, - seat->wlr_seat->keyboard_state.focused_surface); +void seatop_render(struct sway_seat *seat, struct sway_output *output, + pixman_region32_t *damage) { + if (seat->seatop_impl && seat->seatop_impl->render) { + seat->seatop_impl->render(seat, output, damage); + } } diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c deleted file mode 100644 index df8232afc..000000000 --- a/sway/input/seatop_default.c +++ /dev/null @@ -1,1155 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "gesture.h" -#include "sway/desktop/transaction.h" -#include "sway/input/cursor.h" -#include "sway/input/seat.h" -#include "sway/input/tablet.h" -#include "sway/layers.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/scene_descriptor.h" -#include "sway/tree/view.h" -#include "sway/tree/workspace.h" -#include "log.h" -#include "util.h" -#if WLR_HAS_XWAYLAND -#include "sway/xwayland.h" -#endif - -struct seatop_default_event { - struct sway_node *previous_node; - uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; - size_t pressed_button_count; - struct gesture_tracker gestures; -}; - -/*-----------------------------------------\ - * 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); - if (!siblings) { - return false; - } - 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->pending.parent; - } - return true; -} - -static enum wlr_edges find_edge(struct sway_container *cont, - struct wlr_surface *surface, struct sway_cursor *cursor) { - if (!cont->view || (surface && cont->view->surface != surface)) { - return WLR_EDGE_NONE; - } - if (cont->pending.border == B_NONE || !cont->pending.border_thickness || - cont->pending.border == B_CSD) { - return WLR_EDGE_NONE; - } - if (cont->pending.fullscreen_mode) { - return WLR_EDGE_NONE; - } - - enum wlr_edges edge = 0; - if (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) { - edge |= WLR_EDGE_LEFT; - } - if (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) { - edge |= WLR_EDGE_TOP; - } - if (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) { - edge |= WLR_EDGE_RIGHT; - } - if (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.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. - */ -enum wlr_edges find_resize_edge(struct sway_container *cont, - struct wlr_surface *surface, struct sway_cursor *cursor) { - enum wlr_edges edge = find_edge(cont, surface, 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++; -} - -/*-------------------------------------------\ - * Functions used by handle_tablet_tool_tip / - *-----------------------------------------*/ - -static void handle_tablet_tool_tip(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec, - enum wlr_tablet_tool_tip_state state) { - if (state == WLR_TABLET_TOOL_TIP_UP) { - wlr_tablet_v2_tablet_tool_notify_up(tool->tablet_v2_tool); - return; - } - - 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 (!sway_assert(surface, - "Expected null-surface tablet input to route through pointer emulation")) { - return; - } - - struct sway_container *cont = node && node->type == N_CONTAINER ? - node->sway_container : NULL; - - struct wlr_layer_surface_v1 *layer; -#if WLR_HAS_XWAYLAND - struct wlr_xwayland_surface *xsurface; -#endif - if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && - layer->current.keyboard_interactive) { - // Handle tapping a layer surface - seat_set_focus_layer(seat, layer); - transaction_commit_dirty(); - } else if (cont) { - bool is_floating_or_child = container_is_floating_or_child(cont); - bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - bool mod_pressed = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - - // Handle beginning floating move - if (is_floating_or_child && !is_fullscreen_or_child && mod_pressed) { - seat_set_focus_container(seat, - seat_get_focus_inactive_view(seat, &cont->node)); - seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); - return; - } - - // Handle moving a tiling container - if (config->tiling_drag && mod_pressed && !is_floating_or_child && - cont->pending.fullscreen_mode == FULLSCREEN_NONE) { - seatop_begin_move_tiling(seat, cont); - return; - } - - // Handle tapping on a container surface - seat_set_focus_container(seat, cont); - seatop_begin_down(seat, node->sway_container, sx, sy); - } -#if WLR_HAS_XWAYLAND - // Handle tapping on an xwayland unmanaged view - else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && - xsurface->override_redirect && - wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface, false); - transaction_commit_dirty(); - } -#endif - - wlr_tablet_v2_tablet_tool_notify_down(tool->tablet_v2_tool); - wlr_tablet_tool_v2_start_implicit_grab(tool->tablet_v2_tool); -} - -/*----------------------------------\ - * Functions used by handle_button / - *--------------------------------*/ - -static bool trigger_pointer_button_binding(struct sway_seat *seat, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state, uint32_t modifiers, - bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { - // We can reach this for non-pointer devices if we're currently emulating - // pointer input for one. Emulated input should not trigger bindings. The - // device can be NULL if this is synthetic (e.g. swaymsg-generated) input. - if (device && device->type != WLR_INPUT_DEVICE_POINTER) { - return false; - } - - struct seatop_default_event *e = seat->seatop_data; - - char *device_identifier = device ? input_device_get_identifier(device) - : strdup("*"); - struct sway_binding *binding = NULL; - if (state == WL_POINTER_BUTTON_STATE_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 true; - } - - return false; -} - -static void handle_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state) { - 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, surface, cursor) : WLR_EDGE_NONE; - enum wlr_edges resize_edge = cont && edge ? - find_resize_edge(cont, surface, 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; - - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - - bool mod_pressed = modifiers & config->floating_mod; - uint32_t mod_move_btn = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; - uint32_t mod_resize_btn = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; - bool mod_move_btn_pressed = mod_pressed && button == mod_move_btn; - bool mod_resize_btn_pressed = mod_pressed && button == mod_resize_btn; - bool titlebar_left_btn_pressed = on_titlebar && button == BTN_LEFT; - - // Handle mouse bindings - if (trigger_pointer_button_binding(seat, device, button, state, modifiers, - on_titlebar, on_border, on_contents, on_workspace)) { - return; - } - - // Handle clicking an empty workspace - if (node && node->type == N_WORKSPACE) { - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - seat_set_focus(seat, node); - transaction_commit_dirty(); - } - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - // Handle clicking a layer surface and its popups/subsurfaces - struct wlr_layer_surface_v1 *layer = NULL; - if ((layer = toplevel_layer_surface_from_surface(surface))) { - if (layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, layer); - transaction_commit_dirty(); - } - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - seatop_begin_down_on_surface(seat, surface, sx, sy); - } - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - // Handle tiling resize via border - if (cont && resize_edge && button == BTN_LEFT && - state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) { - // If a resize is triggered on a tabbed or stacked container, change - // focus to the tab which already had inactive focus -- otherwise, we'd - // change the active tab when the user probably just wanted to resize. - struct sway_container *cont_to_focus = cont; - enum sway_container_layout layout = container_parent_layout(cont); - if (layout == L_TABBED || layout == L_STACKED) { - cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node); - } - - seat_set_focus_container(seat, cont_to_focus); - seatop_begin_resize_tiling(seat, cont, edge); - return; - } - - // Handle tiling resize via mod - if (cont && !is_floating_or_child && mod_pressed && mod_resize_btn_pressed && - state == WL_POINTER_BUTTON_STATE_PRESSED) { - edge = 0; - edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->pending.y + cont->pending.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 changing focus when clicking on a container - if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { - // Default case: focus the container that was just clicked. - node = &cont->node; - - // If the container is a tab/stacked container and the click happened - // on a tab, switch to the tab. If the tab contents were already - // focused, focus the tab container itself. If the tab container was - // already focused, cycle back to focusing the tab contents. - if (on_titlebar) { - struct sway_container *focus = seat_get_focused_container(seat); - if (focus == cont || !container_has_ancestor(focus, cont)) { - node = seat_get_focus_inactive(seat, &cont->node); - } - } - - seat_set_focus(seat, node); - transaction_commit_dirty(); - } - - // Handle beginning floating move - if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WL_POINTER_BUTTON_STATE_PRESSED && - (mod_move_btn_pressed || titlebar_left_btn_pressed)) { - seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); - return; - } - - // Handle beginning floating resize - if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WL_POINTER_BUTTON_STATE_PRESSED) { - // Via border - if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { - seat_set_focus_container(seat, cont); - seatop_begin_resize_floating(seat, cont, resize_edge); - return; - } - - // Via mod+click - if (mod_resize_btn_pressed) { - struct sway_container *floater = container_toplevel_ancestor(cont); - edge = 0; - edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - seat_set_focus_container(seat, floater); - seatop_begin_resize_floating(seat, floater, edge); - return; - } - } - - // Handle moving a tiling container - if (config->tiling_drag && (mod_move_btn_pressed || titlebar_left_btn_pressed) && - state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && - cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { - // If moving a container by its title bar, use a threshold for the drag - if (!mod_pressed && config->tiling_drag_threshold > 0) { - seatop_begin_move_tiling_threshold(seat, cont); - } else { - seatop_begin_move_tiling(seat, cont); - } - return; - } - - // Handle mousedown on a container surface - if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { - seatop_begin_down(seat, cont, sx, sy); - seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED); - return; - } - - // Handle clicking a container surface or decorations - if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - -#if WLR_HAS_XWAYLAND - // Handle clicking on xwayland unmanaged view - struct wlr_xwayland_surface *xsurface; - if (surface && - (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && - xsurface->override_redirect && - wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface, false); - transaction_commit_dirty(); - seat_pointer_notify_button(seat, time_msec, button, state); - } -#endif - - seat_pointer_notify_button(seat, time_msec, button, state); -} - -/*------------------------------------------\ - * Functions used by handle_pointer_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); - - // This is the case if a layer-shell surface is hovered. - // If it's on another output, focus the active workspace there. - if (!hovered_node) { - struct wlr_output *wlr_output = wlr_output_layout_output_at( - root->output_layout, seat->cursor->cursor->x, seat->cursor->cursor->y); - if (wlr_output == NULL) { - return; - } - - struct wlr_surface *surface = NULL; - double sx, sy; - node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y, - &surface, &sx, &sy); - - // Focus topmost layer surface - struct wlr_layer_surface_v1 *layer = NULL; - if ((layer = toplevel_layer_surface_from_surface(surface)) && - layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, layer); - transaction_commit_dirty(); - return; - } - - struct sway_output *hovered_output = wlr_output->data; - if (focus && hovered_output != node_get_output(focus)) { - struct sway_workspace *ws = output_get_active_workspace(hovered_output); - seat_set_focus(seat, &ws->node); - transaction_commit_dirty(); - } - return; - } - - // 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)); - transaction_commit_dirty(); - } - 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); - transaction_commit_dirty(); - } - } -} - -static void handle_pointer_motion(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, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - - if (config->focus_follows_mouse != FOLLOWS_NO) { - check_focus_follows_mouse(seat, e, node); - } - - if (surface) { - if (seat_is_input_allowed(seat, surface)) { - wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); - } - } else { - cursor_update_image(cursor, node); - wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); - } - - drag_icons_update_position(seat); - - e->previous_node = node; -} - -static void handle_tablet_tool_motion(struct sway_seat *seat, - struct sway_tablet_tool *tool, 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, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - - if (config->focus_follows_mouse != FOLLOWS_NO) { - check_focus_follows_mouse(seat, e, node); - } - - if (surface) { - if (seat_is_input_allowed(seat, surface)) { - wlr_tablet_v2_tablet_tool_notify_proximity_in(tool->tablet_v2_tool, - tool->tablet->tablet_v2, surface); - wlr_tablet_v2_tablet_tool_notify_motion(tool->tablet_v2_tool, sx, sy); - } - } else { - cursor_update_image(cursor, node); - wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); - } - - drag_icons_update_position(seat); - - e->previous_node = node; -} - -static void handle_touch_down(struct sway_seat *seat, - struct wlr_touch_down_event *event, double lx, double ly) { - struct wlr_surface *surface = NULL; - struct wlr_seat *wlr_seat = seat->wlr_seat; - struct sway_cursor *cursor = seat->cursor; - double sx, sy; - node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy); - - if (surface && wlr_surface_accepts_touch(surface, wlr_seat)) { - if (seat_is_input_allowed(seat, surface)) { - cursor->simulating_pointer_from_touch = false; - seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly); - } - } else if (!cursor->simulating_pointer_from_touch && - (!surface || seat_is_input_allowed(seat, surface))) { - // Fallback to cursor simulation. - // The pointer_touch_id state is needed, so drags are not aborted when over - // a surface supporting touch and multi touch events don't interfere. - cursor->simulating_pointer_from_touch = true; - cursor->pointer_touch_id = seat->touch_id; - double dx, dy; - dx = seat->touch_x - cursor->cursor->x; - dy = seat->touch_y - cursor->cursor->y; - pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, - dx, dy); - dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, - BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); - } -} - -/*----------------------------------------\ - * Functions used by handle_pointer_axis / - *--------------------------------------*/ - -static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { - switch (event->orientation) { - case WL_POINTER_AXIS_VERTICAL_SCROLL: - return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; - case WL_POINTER_AXIS_HORIZONTAL_SCROLL: - return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; - default: - sway_log(SWAY_DEBUG, "Unknown axis orientation"); - return 0; - } -} - -static void handle_pointer_axis(struct sway_seat *seat, - struct wlr_pointer_axis_event *event) { - struct sway_input_device *input_device = - event->pointer ? event->pointer->base.data : NULL; - struct input_config *ic = - input_device ? input_device_get_config(input_device) : NULL; - struct sway_cursor *cursor = seat->cursor; - 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, surface, 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->pending.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)) { - struct sway_node *new_focus; - enum sway_container_layout layout = container_parent_layout(cont); - if (layout == L_TABBED || layout == L_STACKED) { - struct sway_node *tabcontainer = node_get_parent(node); - 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) + - roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); - if (desired < 0) { - desired = 0; - } else if (desired >= siblings->length) { - desired = siblings->length - 1; - } - - struct sway_container *new_sibling_con = siblings->items[desired]; - struct sway_node *new_sibling = &new_sibling_con->node; - // Use the focused child of the tabbed/stacked container, not the - // container the user scrolled on. - new_focus = seat_get_focus_inactive(seat, new_sibling); - } else { - new_focus = seat_get_focus_inactive(seat, &cont->node); - } - - seat_set_focus(seat, new_focus); - transaction_commit_dirty(); - handled = true; - } - - // Handle mouse bindings - x11 mouse buttons 4-7 - release event - binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, - 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, - roundf(scroll_factor * event->delta_discrete), event->source, - event->relative_direction); - } -} - -/*------------------------------------\ - * Functions used by gesture support / - *----------------------------------*/ - -/** - * Check gesture binding for a specific gesture type and finger count. - * Returns true if binding is present, false otherwise - */ -static bool gesture_binding_check(list_t *bindings, enum gesture_type type, - uint8_t fingers, struct sway_input_device *device) { - char *input = - device ? input_device_get_identifier(device->wlr_device) : strdup("*"); - - for (int i = 0; i < bindings->length; ++i) { - struct sway_gesture_binding *binding = bindings->items[i]; - - // Check type and finger count - if (!gesture_check(&binding->gesture, type, fingers)) { - continue; - } - - // Check that input matches - if (strcmp(binding->input, "*") != 0 && - strcmp(binding->input, input) != 0) { - continue; - } - - free(input); - - return true; - } - - free(input); - - return false; -} - -/** - * Return the gesture binding which matches gesture type, finger count - * and direction, otherwise return null. - */ -static struct sway_gesture_binding* gesture_binding_match( - list_t *bindings, struct gesture *gesture, const char *input) { - struct sway_gesture_binding *current = NULL; - - // Find best matching binding - for (int i = 0; i < bindings->length; ++i) { - struct sway_gesture_binding *binding = bindings->items[i]; - bool exact = binding->flags & BINDING_EXACT; - - // Check gesture matching - if (!gesture_match(&binding->gesture, gesture, exact)) { - continue; - } - - // Check input matching - if (strcmp(binding->input, "*") != 0 && - strcmp(binding->input, input) != 0) { - continue; - } - - // If we already have a match ... - if (current) { - // ... check if input matching is equivalent - if (strcmp(current->input, binding->input) == 0) { - - // ... - do not override an exact binding - if (!exact && current->flags & BINDING_EXACT) { - continue; - } - - // ... - and ensure direction matching is better or equal - if (gesture_compare(¤t->gesture, &binding->gesture) > 0) { - continue; - } - } else if (strcmp(binding->input, "*") == 0) { - // ... do not accept worse input match - continue; - } - } - - // Accept newer or better match - current = binding; - - // If exact binding and input is found, quit search - if (strcmp(current->input, input) == 0 && - gesture_compare(¤t->gesture, gesture) == 0) { - break; - } - } // for all gesture bindings - - return current; -} - -// Wrapper around gesture_tracker_end to use tracker with sway bindings -static struct sway_gesture_binding* gesture_tracker_end_and_match( - struct gesture_tracker *tracker, struct sway_input_device* device) { - // Determine name of input that received gesture - char *input = device - ? input_device_get_identifier(device->wlr_device) - : strdup("*"); - - // Match tracking result to binding - struct gesture *gesture = gesture_tracker_end(tracker); - struct sway_gesture_binding *binding = gesture_binding_match( - config->current_mode->gesture_bindings, gesture, input); - free(gesture); - free(input); - - return binding; -} - -// Small wrapper around seat_execute_command to work on gesture bindings -static void gesture_binding_execute(struct sway_seat *seat, - struct sway_gesture_binding *binding) { - struct sway_binding *dummy_binding = - calloc(1, sizeof(struct sway_binding)); - dummy_binding->type = BINDING_GESTURE; - dummy_binding->command = binding->command; - - char *description = gesture_to_string(&binding->gesture); - sway_log(SWAY_DEBUG, "executing gesture binding: %s", description); - free(description); - - seat_execute_command(seat, dummy_binding); - - free(dummy_binding); -} - -static void handle_hold_begin(struct sway_seat *seat, - struct wlr_pointer_hold_begin_event *event) { - // Start tracking gesture if there is a matching binding ... - struct sway_input_device *device = - event->pointer ? event->pointer->base.data : NULL; - list_t *bindings = config->current_mode->gesture_bindings; - if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) { - struct seatop_default_event *seatop = seat->seatop_data; - gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers); - } else { - // ... otherwise forward to client - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_hold_begin( - server.input->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); - } -} - -static void handle_hold_end(struct sway_seat *seat, - struct wlr_pointer_hold_end_event *event) { - // Ensure that gesture is being tracked and was not cancelled - struct seatop_default_event *seatop = seat->seatop_data; - if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_hold_end( - server.input->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->cancelled); - return; - } - if (event->cancelled) { - gesture_tracker_cancel(&seatop->gestures); - return; - } - - // End gesture tracking and execute matched binding - struct sway_input_device *device = - event->pointer ? event->pointer->base.data : NULL; - struct sway_gesture_binding *binding = gesture_tracker_end_and_match( - &seatop->gestures, device); - - if (binding) { - gesture_binding_execute(seat, binding); - } -} - -static void handle_pinch_begin(struct sway_seat *seat, - struct wlr_pointer_pinch_begin_event *event) { - // Start tracking gesture if there is a matching binding ... - struct sway_input_device *device = - event->pointer ? event->pointer->base.data : NULL; - list_t *bindings = config->current_mode->gesture_bindings; - if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) { - struct seatop_default_event *seatop = seat->seatop_data; - gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers); - } else { - // ... otherwise forward to client - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_pinch_begin( - server.input->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); - } -} - -static void handle_pinch_update(struct sway_seat *seat, - struct wlr_pointer_pinch_update_event *event) { - // Update any ongoing tracking ... - struct seatop_default_event *seatop = seat->seatop_data; - if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { - gesture_tracker_update(&seatop->gestures, event->dx, event->dy, - event->scale, event->rotation); - } else { - // ... otherwise forward to client - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_pinch_update( - server.input->pointer_gestures, - cursor->seat->wlr_seat, - event->time_msec, event->dx, event->dy, - event->scale, event->rotation); - } -} - -static void handle_pinch_end(struct sway_seat *seat, - struct wlr_pointer_pinch_end_event *event) { - // Ensure that gesture is being tracked and was not cancelled - struct seatop_default_event *seatop = seat->seatop_data; - if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_pinch_end( - server.input->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->cancelled); - return; - } - if (event->cancelled) { - gesture_tracker_cancel(&seatop->gestures); - return; - } - - // End gesture tracking and execute matched binding - struct sway_input_device *device = - event->pointer ? event->pointer->base.data : NULL; - struct sway_gesture_binding *binding = gesture_tracker_end_and_match( - &seatop->gestures, device); - - if (binding) { - gesture_binding_execute(seat, binding); - } -} - -static void handle_swipe_begin(struct sway_seat *seat, - struct wlr_pointer_swipe_begin_event *event) { - // Start tracking gesture if there is a matching binding ... - struct sway_input_device *device = - event->pointer ? event->pointer->base.data : NULL; - list_t *bindings = config->current_mode->gesture_bindings; - if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) { - struct seatop_default_event *seatop = seat->seatop_data; - gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers); - } else { - // ... otherwise forward to client - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_swipe_begin( - server.input->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); - } -} - -static void handle_swipe_update(struct sway_seat *seat, - struct wlr_pointer_swipe_update_event *event) { - - // Update any ongoing tracking ... - struct seatop_default_event *seatop = seat->seatop_data; - if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { - gesture_tracker_update(&seatop->gestures, - event->dx, event->dy, NAN, NAN); - } else { - // ... otherwise forward to client - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_swipe_update( - server.input->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->dx, event->dy); - } -} - -static void handle_swipe_end(struct sway_seat *seat, - struct wlr_pointer_swipe_end_event *event) { - // Ensure gesture is being tracked and was not cancelled - struct seatop_default_event *seatop = seat->seatop_data; - if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { - struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures, - cursor->seat->wlr_seat, event->time_msec, event->cancelled); - return; - } - if (event->cancelled) { - gesture_tracker_cancel(&seatop->gestures); - return; - } - - // End gesture tracking and execute matched binding - struct sway_input_device *device = - event->pointer ? event->pointer->base.data : NULL; - struct sway_gesture_binding *binding = gesture_tracker_end_and_match( - &seatop->gestures, device); - - if (binding) { - gesture_binding_execute(seat, binding); - } -} - -/*----------------------------------\ - * Functions used by handle_rebase / - *--------------------------------*/ - -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); - - if (surface) { - if (seat_is_input_allowed(seat, surface) && !cursor->hidden) { - wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); - } - } else { - cursor_update_image(cursor, e->previous_node); - wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); - } -} - -static const struct sway_seatop_impl seatop_impl = { - .button = handle_button, - .pointer_motion = handle_pointer_motion, - .pointer_axis = handle_pointer_axis, - .tablet_tool_tip = handle_tablet_tool_tip, - .tablet_tool_motion = handle_tablet_tool_motion, - .hold_begin = handle_hold_begin, - .hold_end = handle_hold_end, - .pinch_begin = handle_pinch_begin, - .pinch_update = handle_pinch_update, - .pinch_end = handle_pinch_end, - .swipe_begin = handle_swipe_begin, - .swipe_update = handle_swipe_update, - .swipe_end = handle_swipe_end, - .touch_down = handle_touch_down, - .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; - - uint32_t time_msec = get_current_time_in_msec(); - seatop_rebase(seat, time_msec); -} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index f64e5a4f2..c2256c9a1 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -1,264 +1,84 @@ -#include +#define _POSIX_C_SOURCE 200809L #include -#include -#include #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/tree/view.h" -#include "sway/desktop/transaction.h" -#include "log.h" - -struct seatop_touch_point_event { - double ref_lx, ref_ly; // touch's x/y at start of op - double ref_con_lx, ref_con_ly; // container's x/y at start of op - int32_t touch_id; - struct wl_list link; -}; struct seatop_down_event { struct sway_container *con; - struct sway_seat *seat; - struct wl_listener surface_destroy; - struct wlr_surface *surface; 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 - struct wl_list point_events; // seatop_touch_point_event::link + bool moved; }; -static void handle_touch_motion(struct sway_seat *seat, - struct wlr_touch_motion_event *event, double lx, double ly) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_down_event *e = seat->seatop_data; - - struct seatop_touch_point_event *point_event; - bool found = false; - wl_list_for_each(point_event, &e->point_events, link) { - if (point_event->touch_id == event->touch_id) { - found = true; - break; - } - } - if (!found) { - return; // Probably not a point_event from this seatop_down - } - - double moved_x = lx - point_event->ref_lx; - double moved_y = ly - point_event->ref_ly; - double sx = point_event->ref_con_lx + moved_x; - double sy = point_event->ref_con_ly + moved_y; - - wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec, - event->touch_id, sx, sy); -} - -static void handle_touch_up(struct sway_seat *seat, - struct wlr_touch_up_event *event) { - struct seatop_down_event *e = seat->seatop_data; - struct seatop_touch_point_event *point_event, *tmp; - - wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { - if (point_event->touch_id == event->touch_id) { - wl_list_remove(&point_event->link); - free(point_event); - break; - } - } - - wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id); - - if (wl_list_empty(&e->point_events)) { - seatop_begin_default(seat); - } -} - -static void handle_touch_down(struct sway_seat *seat, - struct wlr_touch_down_event *event, double lx, double ly) { - struct seatop_down_event *e = seat->seatop_data; - double sx, sy; - struct wlr_surface *surface = NULL; - struct sway_node *focused_node = node_at_coords(seat, seat->touch_x, - seat->touch_y, &surface, &sx, &sy); - - if (!surface || surface != e->surface) { // Must start from the initial surface - return; - } - - struct seatop_touch_point_event *point_event = - calloc(1, sizeof(struct seatop_touch_point_event)); - if (!sway_assert(point_event, "Unable to allocate point_event")) { - return; - } - point_event->touch_id = event->touch_id; - point_event->ref_lx = lx; - point_event->ref_ly = ly; - point_event->ref_con_lx = sx; - point_event->ref_con_ly = sy; - - wl_list_insert(&e->point_events, &point_event->link); - - wlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec, - event->touch_id, sx, sy); - - if (focused_node) { - seat_set_focus(seat, focused_node); - transaction_commit_dirty(); - } -} - -static void handle_touch_cancel(struct sway_seat *seat, - struct wlr_touch_cancel_event *event) { - struct seatop_down_event *e = seat->seatop_data; - struct seatop_touch_point_event *point_event, *tmp; - - wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { - if (point_event->touch_id == event->touch_id) { - wl_list_remove(&point_event->link); - free(point_event); - break; - } - } - - if (e->surface) { - struct wl_client *client = wl_resource_get_client(e->surface->resource); - struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client); - if (seat_client != NULL) { - wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client); - } - } - - if (wl_list_empty(&e->point_events)) { - seatop_begin_default(seat); - } -} - -static void handle_pointer_axis(struct sway_seat *seat, - struct wlr_pointer_axis_event *event) { - struct sway_input_device *input_device = - event->pointer ? event->pointer->base.data : NULL; - struct input_config *ic = - input_device ? input_device_get_config(input_device) : NULL; - float scroll_factor = - (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; - - wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, - roundf(scroll_factor * event->delta_discrete), event->source, - event->relative_direction); -} - -static void handle_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_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_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { - struct seatop_down_event *e = seat->seatop_data; - if (seat_is_input_allowed(seat, e->surface)) { + struct sway_container *con = e->con; + if (seat_is_input_allowed(seat, con->view->surface)) { double moved_x = seat->cursor->cursor->x - e->ref_lx; double moved_y = seat->cursor->cursor->y - e->ref_ly; double sx = e->ref_con_lx + moved_x; 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_tablet_tool_tip(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec, - enum wlr_tablet_tool_tip_state state) { - if (state == WLR_TABLET_TOOL_TIP_UP) { - wlr_tablet_v2_tablet_tool_notify_up(tool->tablet_v2_tool); - seatop_begin_default(seat); - } -} - -static void handle_tablet_tool_motion(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec) { +static void handle_finish(struct sway_seat *seat) { struct seatop_down_event *e = seat->seatop_data; - if (seat_is_input_allowed(seat, e->surface)) { - double moved_x = seat->cursor->cursor->x - e->ref_lx; - double moved_y = seat->cursor->cursor->y - e->ref_ly; - double sx = e->ref_con_lx + moved_x; - double sy = e->ref_con_ly + moved_y; - wlr_tablet_v2_tablet_tool_notify_motion(tool->tablet_v2_tool, sx, sy); + 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_destroy(struct wl_listener *listener, void *data) { - struct seatop_down_event *e = - wl_container_of(listener, e, surface_destroy); - if (e) { - seatop_begin_default(e->seat); - } +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_begin_default(seat); + seatop_abort(seat); } } -static void handle_end(struct sway_seat *seat) { - struct seatop_down_event *e = seat->seatop_data; - wl_list_remove(&e->surface_destroy.link); -} - static const struct sway_seatop_impl seatop_impl = { - .button = handle_button, - .pointer_motion = handle_pointer_motion, - .pointer_axis = handle_pointer_axis, - .tablet_tool_tip = handle_tablet_tool_tip, - .tablet_tool_motion = handle_tablet_tool_motion, - .touch_motion = handle_touch_motion, - .touch_up = handle_touch_up, - .touch_down = handle_touch_down, - .touch_cancel = handle_touch_cancel, + .motion = handle_motion, + .finish = handle_finish, + .abort = handle_abort, .unref = handle_unref, - .end = handle_end, - .allow_set_cursor = true, }; -void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - double sx, double sy) { - seatop_begin_down_on_surface(seat, con->view->surface, sx, sy); - struct seatop_down_event *e = seat->seatop_data; - e->con = con; - - container_raise_floating(con); - transaction_commit_dirty(); -} - -void seatop_begin_touch_down(struct sway_seat *seat, - struct wlr_surface *surface, struct wlr_touch_down_event *event, - double sx, double sy, double lx, double ly) { - seatop_begin_down_on_surface(seat, surface, sx, sy); - handle_touch_down(seat, event, lx, ly); -} - -void seatop_begin_down_on_surface(struct sway_seat *seat, - struct wlr_surface *surface, double sx, double sy) { - seatop_end(seat); +void seatop_begin_down(struct sway_seat *seat, + struct sway_container *con, uint32_t button, int sx, int sy) { + seatop_abort(seat); struct seatop_down_event *e = calloc(1, sizeof(struct seatop_down_event)); - if (!sway_assert(e, "Unable to allocate e")) { + if (!e) { return; } - e->con = NULL; - e->seat = seat; - e->surface = surface; - wl_signal_add(&e->surface->events.destroy, &e->surface_destroy); - e->surface_destroy.notify = handle_destroy; + e->con = con; e->ref_lx = seat->cursor->cursor->x; e->ref_ly = seat->cursor->cursor->y; e->ref_con_lx = sx; e->ref_con_ly = sy; - wl_list_init(&e->point_events); + 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 83668d88e..08e3a5a4e 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -1,80 +1,65 @@ +#define _POSIX_C_SOURCE 200809L #include -#include "sway/desktop/transaction.h" +#include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" struct seatop_move_floating_event { struct sway_container *con; - double dx, dy; // cursor offset in container }; -static void finalize_move(struct sway_seat *seat) { +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_finish(struct sway_seat *seat) { 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->pending.x, e->con->pending.y); - transaction_commit_dirty(); - - seatop_begin_default(seat); + container_floating_move_to(e->con, e->con->x, e->con->y); + cursor_set_image(seat->cursor, "left_ptr", NULL); } -static void handle_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state) { - if (seat->cursor->pressed_button_count == 0) { - finalize_move(seat); - } -} - -static void handle_tablet_tool_tip(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec, - enum wlr_tablet_tool_tip_state state) { - if (state == WLR_TABLET_TOOL_TIP_UP) { - finalize_move(seat); - } -} -static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { - struct seatop_move_floating_event *e = seat->seatop_data; - struct wlr_cursor *cursor = seat->cursor->cursor; - container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); - transaction_commit_dirty(); +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_move_floating_event *e = seat->seatop_data; if (e->con == con) { - seatop_begin_default(seat); + seatop_abort(seat); } } static const struct sway_seatop_impl seatop_impl = { - .button = handle_button, - .pointer_motion = handle_pointer_motion, - .tablet_tool_tip = handle_tablet_tool_tip, + .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) { - seatop_end(seat); + struct sway_container *con, uint32_t button) { + seatop_abort(seat); - struct sway_cursor *cursor = seat->cursor; struct seatop_move_floating_event *e = calloc(1, sizeof(struct seatop_move_floating_event)); if (!e) { return; } e->con = con; - e->dx = cursor->cursor->x - con->pending.x; - e->dy = cursor->cursor->y - con->pending.y; seat->seatop_impl = &seatop_impl; seat->seatop_data = e; + seat->seatop_button = button; container_raise_floating(con); - transaction_commit_dirty(); - cursor_set_image(cursor, "grab", NULL); - wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); + cursor_set_image(seat->cursor, "grab", NULL); } diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index c525b77a9..1e548f5ae 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -1,10 +1,10 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include -#include "sway/desktop/transaction.h" +#include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" -#include "sway/ipc-server.h" #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/node.h" @@ -14,25 +14,31 @@ // Thickness of the dropzone when dragging to the edge of a layout container #define DROP_LAYOUT_BORDER 30 -// Thickness of indicator when dropping onto a titlebar. This should be a -// multiple of 2. -#define DROP_SPLIT_INDICATOR 10 - struct seatop_move_tiling_event { struct sway_container *con; struct sway_node *target_node; enum wlr_edges target_edge; + struct wlr_box drop_box; double ref_lx, ref_ly; // cursor's x/y at start of op bool threshold_reached; - bool split_target; - bool insert_after_target; - struct wlr_scene_rect *indicator_rect; }; -static void handle_end(struct sway_seat *seat) { +static void handle_render(struct sway_seat *seat, + struct sway_output *output, pixman_region32_t *damage) { struct seatop_move_tiling_event *e = seat->seatop_data; - wlr_scene_node_destroy(&e->indicator_rect->node); - e->indicator_rect = NULL; + if (!e->threshold_reached) { + return; + } + if (e->target_node && node_get_output(e->target_node) == output) { + float color[4]; + memcpy(&color, config->border_colors.focused.indicator, + sizeof(float) * 4); + premultiply_alpha(color, 0.5); + 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); + } } static void handle_motion_prethreshold(struct sway_seat *seat) { @@ -53,7 +59,6 @@ static void handle_motion_prethreshold(struct sway_seat *seat) { // If the threshold has been exceeded, start the actual drag if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { - wlr_scene_node_set_enabled(&e->indicator_rect->node, true); e->threshold_reached = true; cursor_set_image(seat->cursor, "grab", NULL); } @@ -85,86 +90,15 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge, } } -static void split_border(double pos, int offset, int len, int n_children, - int avoid, int *out_pos, bool *out_after) { - int region = 2 * n_children * (pos - offset) / len; - // If the cursor is over the right side of a left-adjacent titlebar, or the - // left side of a right-adjacent titlebar, it's position when dropped will - // be the same. To avoid this, shift the region for adjacent containers. - if (avoid >= 0) { - if (region == 2 * avoid - 1 || region == 2 * avoid) { - region--; - } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) { - region++; - } - } - - int child_index = (region + 1) / 2; - *out_after = region % 2; - // When dropping at the beginning or end of a container, show the drop - // region within the container boundary, otherwise show it on top of the - // border between two titlebars. - if (child_index == 0) { - *out_pos = offset; - } else if (child_index == n_children) { - *out_pos = offset + len - DROP_SPLIT_INDICATOR; - } else { - *out_pos = offset + child_index * len / n_children - - DROP_SPLIT_INDICATOR / 2; - } -} - -static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, - struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) { - struct sway_container *con = node->sway_container; - struct sway_node *parent = &con->pending.parent->node; - int title_height = container_titlebar_height(); - struct wlr_box box; - int n_children, avoid_index; - enum sway_container_layout layout = - parent ? node_get_layout(parent) : L_NONE; - if (layout == L_TABBED || layout == L_STACKED) { - node_get_box(parent, &box); - n_children = node_get_children(parent)->length; - avoid_index = list_find(node_get_children(parent), avoid); - } else { - node_get_box(node, &box); - n_children = 1; - avoid_index = -1; - } - if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) { - // Drop into stacked titlebars. - title_box->width = box.width; - title_box->height = DROP_SPLIT_INDICATOR; - title_box->x = box.x; - split_border(cursor->y, box.y, title_height * n_children, - n_children, avoid_index, &title_box->y, after); - return true; - } else if (layout != L_STACKED && cursor->y < box.y + title_height) { - // Drop into side-by-side titlebars. - title_box->width = DROP_SPLIT_INDICATOR; - title_box->height = title_height; - title_box->y = box.y; - split_border(cursor->x, box.x, box.width, n_children, - avoid_index, &title_box->x, after); - return true; - } - return false; -} - -static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) { - wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y); - wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height); -} - static void handle_motion_postthreshold(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; - e->split_target = false; struct wlr_surface *surface = NULL; double sx, sy; struct sway_cursor *cursor = seat->cursor; struct sway_node *node = node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + // Damage the old location + desktop_damage_box(&e->drop_box); if (!node) { // Eg. hovered over a layer surface such as swaybar @@ -174,80 +108,44 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } if (node->type == N_WORKSPACE) { - // Empty workspace + // Emtpy workspace e->target_node = node; e->target_edge = WLR_EDGE_NONE; - - struct wlr_box drop_box; - workspace_get_box(node->sway_workspace, &drop_box); - update_indicator(e, &drop_box); + workspace_get_box(node->sway_workspace, &e->drop_box); + desktop_damage_box(&e->drop_box); return; } // Deny moving within own workspace if this is the only child struct sway_container *con = node->sway_container; - if (workspace_num_tiling_views(e->con->pending.workspace) == 1 && - con->pending.workspace == e->con->pending.workspace) { + if (workspace_num_tiling_views(e->con->workspace) == 1 && + con->workspace == e->con->workspace) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; return; } - struct wlr_box drop_box = { - .x = con->pending.content_x, - .y = con->pending.content_y, - .width = con->pending.content_width, - .height = con->pending.content_height, - }; - - // Check if the cursor is over a tilebar only if the destination - // container is not a descendant of the source container. - if (!surface && !container_has_ancestor(con, e->con) && - split_titlebar(node, e->con, cursor->cursor, - &drop_box, &e->insert_after_target)) { - // Don't allow dropping over the source container's titlebar - // to give users a chance to cancel a drag operation. - if (con == e->con) { - e->target_node = NULL; - } else { - e->target_node = node; - e->split_target = true; - } - e->target_edge = WLR_EDGE_NONE; - update_indicator(e, &drop_box); - return; - } - // Traverse the ancestors, trying to find a layout container perpendicular // to the edge. Eg. close to the top or bottom of a horiz layout. - int thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER; - int thresh_bottom = con->pending.content_y + - con->pending.content_height - DROP_LAYOUT_BORDER; - int thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER; - int thresh_right = con->pending.content_x + - con->pending.content_width - DROP_LAYOUT_BORDER; while (con) { enum wlr_edges edge = WLR_EDGE_NONE; enum sway_container_layout layout = container_parent_layout(con); - struct wlr_box box; - node_get_box(node_get_parent(&con->node), &box); + struct wlr_box parent; + con->parent ? container_get_box(con->parent, &parent) : + workspace_get_box(con->workspace, &parent); if (layout == L_HORIZ || layout == L_TABBED) { - if (cursor->cursor->y < thresh_top) { + if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { edge = WLR_EDGE_TOP; - box.height = thresh_top - box.y; - } else if (cursor->cursor->y > thresh_bottom) { + } else if (cursor->cursor->y > parent.y + parent.height + - DROP_LAYOUT_BORDER) { edge = WLR_EDGE_BOTTOM; - box.height = box.y + box.height - thresh_bottom; - box.y = thresh_bottom; } } else if (layout == L_VERT || layout == L_STACKED) { - if (cursor->cursor->x < thresh_left) { + if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { edge = WLR_EDGE_LEFT; - box.width = thresh_left - box.x; - } else if (cursor->cursor->x > thresh_right) { + } else if (cursor->cursor->x > parent.x + parent.width + - DROP_LAYOUT_BORDER) { edge = WLR_EDGE_RIGHT; - box.width = box.x + box.width - thresh_right; - box.x = thresh_right; } } if (edge) { @@ -256,15 +154,17 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { e->target_node = node_get_parent(e->target_node); } e->target_edge = edge; - update_indicator(e, &box); + node_get_box(e->target_node, &e->drop_box); + resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); + desktop_damage_box(&e->drop_box); return; } - con = con->pending.parent; + con = con->parent; } // Use the hovered view - but we must be over the actual surface con = node->sway_container; - if (!con->view || !con->view->surface || node == &e->con->node + if (!con->view->surface || node == &e->con->node || node_has_ancestor(node, &e->con->node)) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; @@ -272,23 +172,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } // Find the closest edge - size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3; + size_t thickness = fmin(con->content_width, con->content_height) * 0.3; size_t closest_dist = INT_MAX; size_t dist; e->target_edge = WLR_EDGE_NONE; - if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) { + if ((dist = cursor->cursor->y - con->y) < closest_dist) { closest_dist = dist; e->target_edge = WLR_EDGE_TOP; } - if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) { + if ((dist = cursor->cursor->x - con->x) < closest_dist) { closest_dist = dist; e->target_edge = WLR_EDGE_LEFT; } - if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) { + if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { closest_dist = dist; e->target_edge = WLR_EDGE_RIGHT; } - if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) { + if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { closest_dist = dist; e->target_edge = WLR_EDGE_BOTTOM; } @@ -298,18 +198,25 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } e->target_node = node; - resize_box(&drop_box, e->target_edge, thickness); - update_indicator(e, &drop_box); + e->drop_box.x = con->content_x; + e->drop_box.y = con->content_y; + e->drop_box.width = con->content_width; + e->drop_box.height = con->content_height; + resize_box(&e->drop_box, e->target_edge, thickness); + desktop_damage_box(&e->drop_box); } -static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_tiling_event *e = seat->seatop_data; if (e->threshold_reached) { handle_motion_postthreshold(seat); } else { handle_motion_prethreshold(seat); } - transaction_commit_dirty(); +} + +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, @@ -319,55 +226,38 @@ static bool is_parallel(enum sway_container_layout layout, return layout_is_horiz == edge_is_horiz; } -static void finalize_move(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->target_node) { - seatop_begin_default(seat); + handle_abort(seat); return; } struct sway_container *con = e->con; - struct sway_container *old_parent = con->pending.parent; - struct sway_workspace *old_ws = con->pending.workspace; + struct sway_container *old_parent = con->parent; + struct sway_workspace *old_ws = con->workspace; struct sway_node *target_node = e->target_node; struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? - target_node->sway_workspace : target_node->sway_container->pending.workspace; + target_node->sway_workspace : target_node->sway_container->workspace; enum wlr_edges edge = e->target_edge; int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; - bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER && - !e->split_target; - if (!swap) { - container_detach(con); - } + container_detach(con); // Moving container into empty workspace if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { - con = workspace_add_tiling(new_ws, con); - } else if (e->split_target) { - struct sway_container *target = target_node->sway_container; - enum sway_container_layout layout = container_parent_layout(target); - if (layout != L_TABBED && layout != L_STACKED) { - container_split(target, L_TABBED); - } - container_add_sibling(target, con, e->insert_after_target); - ipc_event_window(con, "move"); + workspace_add_tiling(new_ws, con); } else if (target_node->type == N_CONTAINER) { // Moving container before/after another struct sway_container *target = target_node->sway_container; - if (swap) { - container_swap(target_node->sway_container, con); - } else { - enum sway_container_layout layout = container_parent_layout(target); - if (edge && !is_parallel(layout, edge)) { - enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || - edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ; - container_split(target, new_layout); - } - container_add_sibling(target, con, after); - ipc_event_window(con, "move"); + enum sway_container_layout layout = container_parent_layout(target); + if (edge && !is_parallel(layout, edge)) { + enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || + edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ; + container_split(target, new_layout); } + container_add_sibling(target, con, after); } else { // Target is a workspace which requires splitting enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || @@ -388,10 +278,8 @@ static void finalize_move(struct sway_seat *seat) { int index = list_find(siblings, con); struct sway_container *sibling = index == 0 ? siblings->items[1] : siblings->items[index - 1]; - con->pending.width = sibling->pending.width; - con->pending.height = sibling->pending.height; - con->width_fraction = sibling->width_fraction; - con->height_fraction = sibling->height_fraction; + con->width = sibling->width; + con->height = sibling->height; } arrange_workspace(old_ws); @@ -399,24 +287,7 @@ static void finalize_move(struct sway_seat *seat) { arrange_workspace(new_ws); } - transaction_commit_dirty(); - seatop_begin_default(seat); -} - -static void handle_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state) { - if (seat->cursor->pressed_button_count == 0) { - finalize_move(seat); - } -} - -static void handle_tablet_tool_tip(struct sway_seat *seat, - struct sway_tablet_tool *tool, uint32_t time_msec, - enum wlr_tablet_tool_tip_state state) { - if (state == WLR_TABLET_TOOL_TIP_UP) { - finalize_move(seat); - } + cursor_set_image(seat->cursor, "left_ptr", NULL); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { @@ -425,56 +296,41 @@ 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_begin_default(seat); + seatop_abort(seat); } } static const struct sway_seatop_impl seatop_impl = { - .button = handle_button, - .pointer_motion = handle_pointer_motion, - .tablet_tool_tip = handle_tablet_tool_tip, + .motion = handle_motion, + .finish = handle_finish, + .abort = handle_abort, .unref = handle_unref, - .end = handle_end, + .render = handle_render, }; void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con) { - seatop_end(seat); + struct sway_container *con, uint32_t button) { + seatop_abort(seat); struct seatop_move_tiling_event *e = calloc(1, sizeof(struct seatop_move_tiling_event)); if (!e) { return; } - - const float *indicator = config->border_colors.focused.indicator; - float color[4] = { - indicator[0] * .5, - indicator[1] * .5, - indicator[2] * .5, - indicator[3] * .5, - }; - e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color); - if (!e->indicator_rect) { - free(e); - return; - } - e->con = con; e->ref_lx = seat->cursor->cursor->x; e->ref_ly = seat->cursor->cursor->y; seat->seatop_impl = &seatop_impl; seat->seatop_data = e; + seat->seatop_button = button; container_raise_floating(con); - transaction_commit_dirty(); - wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); } void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con) { - seatop_begin_move_tiling_threshold(seat, con); + struct sway_container *con, uint32_t button) { + seatop_begin_move_tiling_threshold(seat, con, button); 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 bec86e33a..12851b406 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -1,13 +1,12 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include -#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" -#include "sway/tree/container.h" struct seatop_resize_floating_event { struct sway_container *con; @@ -18,21 +17,42 @@ struct seatop_resize_floating_event { double ref_con_lx, ref_con_ly; // container's x/y at start of op }; -static void handle_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state) { - struct seatop_resize_floating_event *e = seat->seatop_data; - struct sway_container *con = e->con; +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 (seat->cursor->pressed_button_count == 0) { - container_set_resizing(con, false); - arrange_container(con); // Send configure w/o resizing hint - transaction_commit_dirty(); - seatop_begin_default(seat); + 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_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_resize_floating_event *e = seat->seatop_data; struct sway_container *con = e->con; enum wlr_edges edge = e->edge; @@ -61,44 +81,22 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { grow_height = e->ref_height * max_multiplier; } - struct sway_container_state *state = &con->current; - double border_width = 0.0; - if (con->current.border == B_NORMAL || con->current.border == B_PIXEL) { - border_width = state->border_thickness * 2; - } - double border_height = 0.0; - if (con->current.border == B_NORMAL) { - border_height += container_titlebar_height(); - border_height += state->border_thickness; - } else if (con->current.border == B_PIXEL) { - border_height += state->border_thickness * 2; - } - // Determine new width/height, and accommodate for floating min/max values double width = e->ref_width + grow_width; double height = e->ref_height + grow_height; int min_width, max_width, min_height, max_height; - floating_calculate_constraints(&min_width, &max_width, - &min_height, &max_height); - width = fmin(width, max_width - border_width); - width = fmax(width, min_width + border_width); - width = fmax(width, 1); - height = fmin(height, max_height - border_height); - height = fmax(height, min_height + border_height); - height = fmax(height, 1); + calculate_floating_constraints(con, &min_width, &max_width, + &min_height, &max_height); + width = fmax(min_width, fmin(width, max_width)); + height = fmax(min_height, fmin(height, max_height)); // Apply the view's min/max size if (con->view) { double view_min_width, view_max_width, view_min_height, view_max_height; view_get_constraints(con->view, &view_min_width, &view_max_width, &view_min_height, &view_max_height); - width = fmin(width, view_max_width - border_width); - width = fmax(width, view_min_width + border_width); - width = fmax(width, 1); - height = fmin(height, view_max_height - border_height); - height = fmax(height, view_min_height + border_height); - height = fmax(height, 1); - + width = fmax(view_min_width, fmin(width, view_max_width)); + height = fmax(view_min_height, fmin(height, view_max_height)); } // Recalculate these, in case we hit a min/max limit @@ -125,42 +123,50 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { // Determine the amounts we need to bump everything relative to the current // size. - int relative_grow_width = width - con->pending.width; - int relative_grow_height = height - con->pending.height; - int relative_grow_x = (e->ref_con_lx + grow_x) - con->pending.x; - int relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y; + int relative_grow_width = width - con->width; + int relative_grow_height = height - con->height; + int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; + int relative_grow_y = (e->ref_con_ly + grow_y) - con->y; // Actually resize stuff - con->pending.x += relative_grow_x; - con->pending.y += relative_grow_y; - con->pending.width += relative_grow_width; - con->pending.height += relative_grow_height; + con->x += relative_grow_x; + con->y += relative_grow_y; + con->width += relative_grow_width; + con->height += relative_grow_height; - con->pending.content_x += relative_grow_x; - con->pending.content_y += relative_grow_y; - con->pending.content_width += relative_grow_width; - con->pending.content_height += relative_grow_height; + con->content_x += relative_grow_x; + con->content_y += relative_grow_y; + con->content_width += relative_grow_width; + con->content_height += relative_grow_height; arrange_container(con); - transaction_commit_dirty(); +} + +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_begin_default(seat); + seatop_abort(seat); } } static const struct sway_seatop_impl seatop_impl = { - .button = handle_button, - .pointer_motion = handle_pointer_motion, + .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, enum wlr_edges edge) { - seatop_end(seat); + struct sway_container *con, uint32_t button, enum wlr_edges edge) { + seatop_abort(seat); struct seatop_resize_floating_event *e = calloc(1, sizeof(struct seatop_resize_floating_event)); @@ -176,20 +182,18 @@ void seatop_begin_resize_floating(struct sway_seat *seat, e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; e->ref_lx = seat->cursor->cursor->x; e->ref_ly = seat->cursor->cursor->y; - e->ref_con_lx = con->pending.x; - e->ref_con_ly = con->pending.y; - e->ref_width = con->pending.width; - e->ref_height = con->pending.height; + e->ref_con_lx = con->x; + e->ref_con_ly = con->y; + e->ref_width = con->width; + e->ref_height = con->height; seat->seatop_impl = &seatop_impl; seat->seatop_data = e; + seat->seatop_button = button; - container_set_resizing(con, true); container_raise_floating(con); - transaction_commit_dirty(); 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_notify_clear_focus(seat->wlr_seat); } diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index be7b3c128..cb0f723d0 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -1,12 +1,9 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" -#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" -#include "sway/tree/arrange.h" -#include "sway/tree/container.h" -#include "sway/tree/view.h" struct seatop_resize_tiling_event { struct sway_container *con; // leaf container @@ -15,10 +12,6 @@ struct seatop_resize_tiling_event { struct sway_container *h_con; struct sway_container *v_con; - // sibling con(s) that will be resized to accommodate - struct sway_container *h_sib; - struct sway_container *v_sib; - enum wlr_edges edge; enum wlr_edges edge_x, edge_y; double ref_lx, ref_ly; // cursor's x/y at start of op @@ -26,53 +19,7 @@ struct seatop_resize_tiling_event { double v_con_orig_height; // height of the vertical ancestor at start }; -static struct sway_container *container_get_resize_sibling( - struct sway_container *con, uint32_t edge) { - if (!con) { - return NULL; - } - - list_t *siblings = container_get_siblings(con); - int index = container_sibling_index(con); - int offset = edge & (WLR_EDGE_TOP | WLR_EDGE_LEFT) ? -1 : 1; - - if (siblings->length == 1) { - return NULL; - } else { - return siblings->items[index + offset]; - } -} - -static void handle_button(struct sway_seat *seat, uint32_t time_msec, - struct wlr_input_device *device, uint32_t button, - enum wl_pointer_button_state state) { - struct seatop_resize_tiling_event *e = seat->seatop_data; - - if (seat->cursor->pressed_button_count == 0) { - if (e->h_con) { - container_set_resizing(e->h_con, false); - container_set_resizing(e->h_sib, false); - if (e->h_con->pending.parent) { - arrange_container(e->h_con->pending.parent); - } else { - arrange_workspace(e->h_con->pending.workspace); - } - } - if (e->v_con) { - container_set_resizing(e->v_con, false); - container_set_resizing(e->v_sib, false); - if (e->v_con->pending.parent) { - arrange_container(e->v_con->pending.parent); - } else { - arrange_workspace(e->v_con->pending.workspace); - } - } - transaction_commit_dirty(); - seatop_begin_default(seat); - } -} - -static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_resize_tiling_event *e = seat->seatop_data; int amount_x = 0; int amount_y = 0; @@ -81,16 +28,16 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { if (e->h_con) { if (e->edge & WLR_EDGE_LEFT) { - amount_x = (e->h_con_orig_width - moved_x) - e->h_con->pending.width; + amount_x = (e->h_con_orig_width - moved_x) - e->h_con->width; } else if (e->edge & WLR_EDGE_RIGHT) { - amount_x = (e->h_con_orig_width + moved_x) - e->h_con->pending.width; + amount_x = (e->h_con_orig_width + moved_x) - e->h_con->width; } } if (e->v_con) { if (e->edge & WLR_EDGE_TOP) { - amount_y = (e->v_con_orig_height - moved_y) - e->v_con->pending.height; + amount_y = (e->v_con_orig_height - moved_y) - e->v_con->height; } else if (e->edge & WLR_EDGE_BOTTOM) { - amount_y = (e->v_con_orig_height + moved_y) - e->v_con->pending.height; + amount_y = (e->v_con_orig_height + moved_y) - e->v_con->height; } } @@ -100,25 +47,33 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { if (amount_y != 0) { container_resize_tiled(e->v_con, e->edge_y, amount_y); } - transaction_commit_dirty(); +} + +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 || e->h_sib == con || e->v_sib == con) { - seatop_begin_default(seat); + if (e->con == con) { + seatop_abort(seat); } } static const struct sway_seatop_impl seatop_impl = { - .button = handle_button, - .pointer_motion = handle_pointer_motion, + .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, enum wlr_edges edge) { - seatop_end(seat); + struct sway_container *con, uint32_t button, enum wlr_edges edge) { + seatop_abort(seat); struct seatop_resize_tiling_event *e = calloc(1, sizeof(struct seatop_resize_tiling_event)); @@ -134,29 +89,21 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { e->edge_x = edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT); e->h_con = container_find_resize_parent(e->con, e->edge_x); - e->h_sib = container_get_resize_sibling(e->h_con, e->edge_x); if (e->h_con) { - container_set_resizing(e->h_con, true); - container_set_resizing(e->h_sib, true); - e->h_con_orig_width = e->h_con->pending.width; + e->h_con_orig_width = e->h_con->width; } } if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { e->edge_y = edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM); e->v_con = container_find_resize_parent(e->con, e->edge_y); - e->v_sib = container_get_resize_sibling(e->v_con, e->edge_y); if (e->v_con) { - container_set_resizing(e->v_con, true); - container_set_resizing(e->v_sib, true); - e->v_con_orig_height = e->v_con->pending.height; + e->v_con_orig_height = e->v_con->height; } } seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - - transaction_commit_dirty(); - wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); + seat->seatop_button = button; } diff --git a/sway/input/switch.c b/sway/input/switch.c deleted file mode 100644 index 6aab4ad0d..000000000 --- a/sway/input/switch.c +++ /dev/null @@ -1,125 +0,0 @@ -#include "sway/config.h" -#include "sway/input/switch.h" -#include "sway/server.h" -#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->wlr = wlr_switch_from_input_device(device->input_device->wlr_device); - switch_device->seat_device = device; - switch_device->state = WLR_SWITCH_STATE_OFF; - wl_list_init(&switch_device->switch_toggle.link); - sway_log(SWAY_DEBUG, "Allocated switch for device"); - - return switch_device; -} - -static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, - enum wlr_switch_state state) { - switch (trigger) { - case SWAY_SWITCH_TRIGGER_ON: - return state == WLR_SWITCH_STATE_ON; - case SWAY_SWITCH_TRIGGER_OFF: - return state == WLR_SWITCH_STATE_OFF; - case SWAY_SWITCH_TRIGGER_TOGGLE: - return true; - } - abort(); // unreachable -} - -static void execute_binding(struct sway_switch *sway_switch) { - struct sway_seat *seat = sway_switch->seat_device->sway_seat; - bool locked = server.session_lock.lock; - - 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 != sway_switch->type) { - continue; - } - if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) { - continue; - } - if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE - || (binding->flags & BINDING_RELOAD) == 0)) { - continue; - } - bool binding_locked = binding->flags & BINDING_LOCKED; - if (!binding_locked && locked) { - continue; - } - - matched_binding = binding; - - if (binding_locked == locked) { - 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); - } -} - -static void handle_switch_toggle(struct wl_listener *listener, void *data) { - struct sway_switch *sway_switch = - wl_container_of(listener, sway_switch, switch_toggle); - struct wlr_switch_toggle_event *event = data; - struct sway_seat *seat = sway_switch->seat_device->sway_seat; - seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); - - struct wlr_input_device *wlr_device = - sway_switch->seat_device->input_device->wlr_device; - char *device_identifier = input_device_get_identifier(wlr_device); - sway_log(SWAY_DEBUG, "%s: type %d state %d", device_identifier, - event->switch_type, event->switch_state); - free(device_identifier); - - sway_switch->type = event->switch_type; - sway_switch->state = event->switch_state; - execute_binding(sway_switch); -} - -void sway_switch_configure(struct sway_switch *sway_switch) { - wl_list_remove(&sway_switch->switch_toggle.link); - wl_signal_add(&sway_switch->wlr->events.toggle, - &sway_switch->switch_toggle); - sway_switch->switch_toggle.notify = handle_switch_toggle; - sway_log(SWAY_DEBUG, "Configured switch for device"); -} - -void sway_switch_destroy(struct sway_switch *sway_switch) { - if (!sway_switch) { - return; - } - wl_list_remove(&sway_switch->switch_toggle.link); - free(sway_switch); -} - -void sway_switch_retrigger_bindings_for_all(void) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - struct sway_seat_device *seat_device; - wl_list_for_each(seat_device, &seat->devices, link) { - struct sway_input_device *input_device = seat_device->input_device; - if (input_device->wlr_device->type != WLR_INPUT_DEVICE_SWITCH) { - continue; - } - execute_binding(seat_device->switch_device); - }; - } -} diff --git a/sway/input/tablet.c b/sway/input/tablet.c deleted file mode 100644 index 19d5debf9..000000000 --- a/sway/input/tablet.c +++ /dev/null @@ -1,376 +0,0 @@ -#include -#include -#include -#include -#include -#include "log.h" -#include "sway/input/cursor.h" -#include "sway/input/seat.h" -#include "sway/input/tablet.h" -#include "sway/server.h" - -#if WLR_HAS_LIBINPUT_BACKEND -#include -#endif - -static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { - struct sway_tablet_pad *pad = - wl_container_of(listener, pad, tablet_destroy); - - pad->tablet = NULL; - - wl_list_remove(&pad->tablet_destroy.link); - wl_list_init(&pad->tablet_destroy.link); -} - -static void attach_tablet_pad(struct sway_tablet_pad *tablet_pad, - struct sway_tablet *tablet) { - sway_log(SWAY_DEBUG, "Attaching tablet pad \"%s\" to tablet tool \"%s\"", - tablet_pad->seat_device->input_device->wlr_device->name, - tablet->seat_device->input_device->wlr_device->name); - - tablet_pad->tablet = tablet; - - wl_list_remove(&tablet_pad->tablet_destroy.link); - tablet_pad->tablet_destroy.notify = handle_pad_tablet_destroy; - wl_signal_add(&tablet->seat_device->input_device->wlr_device->events.destroy, - &tablet_pad->tablet_destroy); -} - -struct sway_tablet *sway_tablet_create(struct sway_seat *seat, - struct sway_seat_device *device) { - struct sway_tablet *tablet = - calloc(1, sizeof(struct sway_tablet)); - if (!sway_assert(tablet, "could not allocate sway tablet for seat")) { - return NULL; - } - - wl_list_insert(&seat->cursor->tablets, &tablet->link); - - device->tablet = tablet; - tablet->seat_device = device; - - return tablet; -} - -void sway_configure_tablet(struct sway_tablet *tablet) { - struct wlr_input_device *device = - tablet->seat_device->input_device->wlr_device; - struct sway_seat *seat = tablet->seat_device->sway_seat; - - seat_configure_xcursor(seat); - - if (!tablet->tablet_v2) { - tablet->tablet_v2 = - wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); - } - -#if WLR_HAS_LIBINPUT_BACKEND - /* Search for a sibling tablet pad */ - if (!wlr_input_device_is_libinput(device)) { - /* We can only do this on libinput devices */ - return; - } - - struct libinput_device_group *group = - libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); - struct sway_tablet_pad *tablet_pad; - wl_list_for_each(tablet_pad, &seat->cursor->tablet_pads, link) { - struct wlr_input_device *pad_device = - tablet_pad->seat_device->input_device->wlr_device; - if (!wlr_input_device_is_libinput(pad_device)) { - continue; - } - - struct libinput_device_group *pad_group = - libinput_device_get_device_group(wlr_libinput_get_device_handle(pad_device)); - - if (pad_group == group) { - attach_tablet_pad(tablet_pad, tablet); - break; - } - } -#endif -} - -void sway_tablet_destroy(struct sway_tablet *tablet) { - if (!tablet) { - return; - } - wl_list_remove(&tablet->link); - free(tablet); -} - -static void handle_tablet_tool_set_cursor(struct wl_listener *listener, void *data) { - struct sway_tablet_tool *tool = - wl_container_of(listener, tool, set_cursor); - struct wlr_tablet_v2_event_cursor *event = data; - - struct sway_cursor *cursor = tool->seat->cursor; - if (!seatop_allows_set_cursor(cursor->seat)) { - return; - } - - struct wl_client *focused_client = NULL; - struct wlr_surface *focused_surface = tool->tablet_v2_tool->focused_surface; - if (focused_surface != NULL) { - focused_client = wl_resource_get_client(focused_surface->resource); - } - - // TODO: check cursor mode - if (focused_client == NULL || - event->seat_client->client != focused_client) { - sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); - return; - } - - cursor_set_image_surface(cursor, event->surface, event->hotspot_x, - event->hotspot_y, focused_client); -} - -static void handle_tablet_tool_destroy(struct wl_listener *listener, void *data) { - struct sway_tablet_tool *tool = - wl_container_of(listener, tool, tool_destroy); - - wl_list_remove(&tool->tool_destroy.link); - wl_list_remove(&tool->set_cursor.link); - - free(tool); -} - -void sway_tablet_tool_configure(struct sway_tablet *tablet, - struct wlr_tablet_tool *wlr_tool) { - struct sway_tablet_tool *tool = - calloc(1, sizeof(struct sway_tablet_tool)); - if (!sway_assert(tool, "could not allocate sway tablet tool for tablet")) { - return; - } - - switch (wlr_tool->type) { - case WLR_TABLET_TOOL_TYPE_LENS: - case WLR_TABLET_TOOL_TYPE_MOUSE: - tool->mode = SWAY_TABLET_TOOL_MODE_RELATIVE; - break; - default: - tool->mode = SWAY_TABLET_TOOL_MODE_ABSOLUTE; - - struct input_config *ic = input_device_get_config( - tablet->seat_device->input_device); - if (!ic) { - break; - } - - for (int i = 0; i < ic->tools->length; i++) { - struct input_config_tool *tool_config = ic->tools->items[i]; - if (tool_config->type == wlr_tool->type) { - tool->mode = tool_config->mode; - break; - } - } - } - - tool->seat = tablet->seat_device->sway_seat; - tool->tablet = tablet; - tool->tablet_v2_tool = - wlr_tablet_tool_create(server.tablet_v2, - tablet->seat_device->sway_seat->wlr_seat, wlr_tool); - - tool->tool_destroy.notify = handle_tablet_tool_destroy; - wl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy); - - tool->set_cursor.notify = handle_tablet_tool_set_cursor; - wl_signal_add(&tool->tablet_v2_tool->events.set_cursor, - &tool->set_cursor); - - wlr_tool->data = tool; -} - -static void handle_tablet_pad_attach(struct wl_listener *listener, - void *data) { - struct sway_tablet_pad *pad = wl_container_of(listener, pad, attach); - struct wlr_tablet_tool *wlr_tool = data; - struct sway_tablet_tool *tool = wlr_tool->data; - - if (!tool) { - return; - } - - attach_tablet_pad(pad, tool->tablet); -} - -static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { - struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); - struct wlr_tablet_pad_ring_event *event = data; - - if (!pad->current_surface) { - return; - } - - wlr_tablet_v2_tablet_pad_notify_ring(pad->tablet_v2_pad, - event->ring, event->position, - event->source == WLR_TABLET_PAD_RING_SOURCE_FINGER, - event->time_msec); -} - -static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { - struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); - struct wlr_tablet_pad_strip_event *event = data; - - if (!pad->current_surface) { - return; - } - - wlr_tablet_v2_tablet_pad_notify_strip(pad->tablet_v2_pad, - event->strip, event->position, - event->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER, - event->time_msec); -} - -static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { - struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); - struct wlr_tablet_pad_button_event *event = data; - - if (!pad->current_surface) { - return; - } - - wlr_tablet_v2_tablet_pad_notify_mode(pad->tablet_v2_pad, - event->group, event->mode, event->time_msec); - - wlr_tablet_v2_tablet_pad_notify_button(pad->tablet_v2_pad, - event->button, event->time_msec, - (enum zwp_tablet_pad_v2_button_state)event->state); -} - -struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, - struct sway_seat_device *device) { - struct sway_tablet_pad *tablet_pad = - calloc(1, sizeof(struct sway_tablet_pad)); - if (!sway_assert(tablet_pad, "could not allocate sway tablet")) { - return NULL; - } - - tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device); - tablet_pad->seat_device = device; - wl_list_init(&tablet_pad->attach.link); - wl_list_init(&tablet_pad->button.link); - wl_list_init(&tablet_pad->strip.link); - wl_list_init(&tablet_pad->ring.link); - wl_list_init(&tablet_pad->surface_destroy.link); - wl_list_init(&tablet_pad->tablet_destroy.link); - - wl_list_insert(&seat->cursor->tablet_pads, &tablet_pad->link); - - return tablet_pad; -} - -void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { - struct wlr_input_device *wlr_device = - tablet_pad->seat_device->input_device->wlr_device; - struct sway_seat *seat = tablet_pad->seat_device->sway_seat; - - if (!tablet_pad->tablet_v2_pad) { - tablet_pad->tablet_v2_pad = - wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device); - } - - wl_list_remove(&tablet_pad->attach.link); - tablet_pad->attach.notify = handle_tablet_pad_attach; - wl_signal_add(&tablet_pad->wlr->events.attach_tablet, - &tablet_pad->attach); - - wl_list_remove(&tablet_pad->button.link); - tablet_pad->button.notify = handle_tablet_pad_button; - wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button); - - wl_list_remove(&tablet_pad->strip.link); - tablet_pad->strip.notify = handle_tablet_pad_strip; - wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip); - - wl_list_remove(&tablet_pad->ring.link); - tablet_pad->ring.notify = handle_tablet_pad_ring; - wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); - -#if WLR_HAS_LIBINPUT_BACKEND - /* Search for a sibling tablet */ - if (!wlr_input_device_is_libinput(wlr_device)) { - /* We can only do this on libinput devices */ - return; - } - - struct libinput_device_group *group = - libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device)); - struct sway_tablet *tool; - wl_list_for_each(tool, &seat->cursor->tablets, link) { - struct wlr_input_device *tablet = - tool->seat_device->input_device->wlr_device; - if (!wlr_input_device_is_libinput(tablet)) { - continue; - } - - struct libinput_device_group *tablet_group = - libinput_device_get_device_group(wlr_libinput_get_device_handle(tablet)); - - if (tablet_group == group) { - attach_tablet_pad(tablet_pad, tool); - break; - } - } -#endif -} - -void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { - if (!tablet_pad) { - return; - } - - wl_list_remove(&tablet_pad->link); - wl_list_remove(&tablet_pad->attach.link); - wl_list_remove(&tablet_pad->button.link); - wl_list_remove(&tablet_pad->strip.link); - wl_list_remove(&tablet_pad->ring.link); - wl_list_remove(&tablet_pad->surface_destroy.link); - wl_list_remove(&tablet_pad->tablet_destroy.link); - - free(tablet_pad); -} - -static void handle_pad_tablet_surface_destroy(struct wl_listener *listener, - void *data) { - struct sway_tablet_pad *tablet_pad = - wl_container_of(listener, tablet_pad, surface_destroy); - - sway_tablet_pad_set_focus(tablet_pad, NULL); -} - -void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, - struct wlr_surface *surface) { - if (!tablet_pad || !tablet_pad->tablet) { - return; - } - - if (surface == tablet_pad->current_surface) { - return; - } - - /* Leave current surface */ - if (tablet_pad->current_surface) { - wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, - tablet_pad->current_surface); - wl_list_remove(&tablet_pad->surface_destroy.link); - wl_list_init(&tablet_pad->surface_destroy.link); - tablet_pad->current_surface = NULL; - } - - if (surface == NULL || - !wlr_surface_accepts_tablet_v2(surface, tablet_pad->tablet->tablet_v2)) { - return; - } - - wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad->tablet_v2_pad, - tablet_pad->tablet->tablet_v2, surface); - - tablet_pad->current_surface = surface; - tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; - wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); -} diff --git a/sway/input/text_input.c b/sway/input/text_input.c deleted file mode 100644 index e496bff1b..000000000 --- a/sway/input/text_input.c +++ /dev/null @@ -1,687 +0,0 @@ -#include -#include -#include "log.h" -#include "sway/input/seat.h" -#include "sway/scene_descriptor.h" -#include "sway/tree/root.h" -#include "sway/tree/view.h" -#include "sway/output.h" -#include "sway/input/text_input.h" -#include "sway/input/text_input_popup.h" -#include "sway/layers.h" -#include "sway/server.h" -#include - -static struct sway_text_input *relay_get_focusable_text_input( - struct sway_input_method_relay *relay) { - struct sway_text_input *text_input = NULL; - wl_list_for_each(text_input, &relay->text_inputs, link) { - if (text_input->pending_focused_surface) { - return text_input; - } - } - return NULL; -} - -static struct sway_text_input *relay_get_focused_text_input( - struct sway_input_method_relay *relay) { - struct sway_text_input *text_input = NULL; - wl_list_for_each(text_input, &relay->text_inputs, link) { - if (text_input->input->focused_surface) { - return text_input; - } - } - return NULL; -} - -static void handle_im_commit(struct wl_listener *listener, void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_commit); - - struct sway_text_input *text_input = relay_get_focused_text_input(relay); - if (!text_input) { - return; - } - if (relay->input_method->current.preedit.text) { - wlr_text_input_v3_send_preedit_string(text_input->input, - relay->input_method->current.preedit.text, - relay->input_method->current.preedit.cursor_begin, - relay->input_method->current.preedit.cursor_end); - } - if (relay->input_method->current.commit_text) { - wlr_text_input_v3_send_commit_string(text_input->input, - relay->input_method->current.commit_text); - } - if (relay->input_method->current.delete.before_length - || relay->input_method->current.delete.after_length) { - wlr_text_input_v3_send_delete_surrounding_text(text_input->input, - relay->input_method->current.delete.before_length, - relay->input_method->current.delete.after_length); - } - wlr_text_input_v3_send_done(text_input->input); -} - -static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_keyboard_grab_destroy); - struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = relay->input_method->keyboard_grab; - struct wlr_seat *wlr_seat = keyboard_grab->input_method->seat; - wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); - - if (keyboard_grab->keyboard) { - // send modifier state to original client - wlr_seat_set_keyboard(wlr_seat, keyboard_grab->keyboard); - wlr_seat_keyboard_notify_modifiers(wlr_seat, - &keyboard_grab->keyboard->modifiers); - } -} - -static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_grab_keyboard); - struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; - - // send modifier state to grab - struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); - wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, - active_keyboard); - - wl_signal_add(&keyboard_grab->events.destroy, - &relay->input_method_keyboard_grab_destroy); - relay->input_method_keyboard_grab_destroy.notify = - handle_im_keyboard_grab_destroy; -} - -static void text_input_set_pending_focused_surface( - struct sway_text_input *text_input, struct wlr_surface *surface) { - wl_list_remove(&text_input->pending_focused_surface_destroy.link); - text_input->pending_focused_surface = surface; - - if (surface) { - wl_signal_add(&surface->events.destroy, - &text_input->pending_focused_surface_destroy); - } else { - wl_list_init(&text_input->pending_focused_surface_destroy.link); - } -} - -static void handle_im_destroy(struct wl_listener *listener, void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_destroy); - wl_list_remove(&relay->input_method_commit.link); - wl_list_remove(&relay->input_method_grab_keyboard.link); - wl_list_remove(&relay->input_method_destroy.link); - wl_list_remove(&relay->input_method_new_popup_surface.link); - relay->input_method = NULL; - struct sway_text_input *text_input = relay_get_focused_text_input(relay); - if (text_input) { - // keyboard focus is still there, so keep the surface at hand in case - // the input method returns - text_input_set_pending_focused_surface(text_input, - text_input->input->focused_surface); - wlr_text_input_v3_send_leave(text_input->input); - } -} - -static void constrain_popup(struct sway_input_popup *popup) { - struct sway_text_input *text_input = - relay_get_focused_text_input(popup->relay); - - if (!popup->desc.relative) { - return; - } - - struct wlr_box parent = {0}; - wlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y); - - struct wlr_box geo = {0}; - struct wlr_output *output; - - if (popup->desc.view) { - struct sway_view *view = popup->desc.view; - output = wlr_output_layout_output_at(root->output_layout, - view->container->pending.content_x + view->geometry.x, - view->container->pending.content_y + view->geometry.y); - - parent.width = view->geometry.width; - parent.height = view->geometry.height; - geo = view->geometry; - } else { - output = popup->fixed_output; - } - - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, output, &output_box); - - bool cursor_rect = text_input->input->current.features & - WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; - struct wlr_box cursor_area; - if (cursor_rect) { - cursor_area = text_input->input->current.cursor_rectangle; - } else { - cursor_area = (struct wlr_box) { - .width = parent.width, - .height = parent.height, - }; - } - - int popup_width = popup->popup_surface->surface->current.width; - int popup_height = popup->popup_surface->surface->current.height; - int x1 = parent.x + cursor_area.x; - int x2 = parent.x + cursor_area.x + cursor_area.width; - int y1 = parent.y + cursor_area.y; - int y2 = parent.y + cursor_area.y + cursor_area.height; - int x = x1; - int y = y2; - - int available_right = output_box.x + output_box.width - x1; - int available_left = x2 - output_box.x; - if (available_right < popup_width && available_left > available_right) { - x = x2 - popup_width; - } - - int available_down = output_box.y + output_box.height - y2; - int available_up = y1 - output_box.y; - if (available_down < popup_height && available_up > available_down) { - y = y1 - popup_height; - } - - wlr_scene_node_set_position(popup->desc.relative, x - parent.x - geo.x, y - parent.y - geo.y); - if (cursor_rect) { - struct wlr_box box = { - .x = x1 - x, - .y = y1 - y, - .width = cursor_area.width, - .height = cursor_area.height, - }; - wlr_input_popup_surface_v2_send_text_input_rectangle( - popup->popup_surface, &box); - } - - if (popup->scene_tree) { - wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); - } -} - -static void input_popup_set_focus(struct sway_input_popup *popup, - struct wlr_surface *surface); - -static void relay_send_im_state(struct sway_input_method_relay *relay, - struct wlr_text_input_v3 *input) { - struct wlr_input_method_v2 *input_method = relay->input_method; - if (!input_method) { - sway_log(SWAY_INFO, "Sending IM_DONE but im is gone"); - return; - } - // TODO: only send each of those if they were modified - if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) { - wlr_input_method_v2_send_surrounding_text(input_method, - input->current.surrounding.text, input->current.surrounding.cursor, - input->current.surrounding.anchor); - } - wlr_input_method_v2_send_text_change_cause(input_method, - input->current.text_change_cause); - if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) { - wlr_input_method_v2_send_content_type(input_method, - input->current.content_type.hint, - input->current.content_type.purpose); - } - - struct sway_text_input *text_input = relay_get_focused_text_input(relay); - - struct sway_input_popup *popup; - wl_list_for_each(popup, &relay->input_popups, link) { - if (text_input != NULL) { - input_popup_set_focus(popup, text_input->input->focused_surface); - } else { - input_popup_set_focus(popup, NULL); - } - } - wlr_input_method_v2_send_done(input_method); - // TODO: pass intent, display popup size -} - -static void handle_text_input_enable(struct wl_listener *listener, void *data) { - struct sway_text_input *text_input = wl_container_of(listener, text_input, - text_input_enable); - if (text_input->input->focused_surface == NULL) { - sway_log(SWAY_DEBUG, "Enabling text input, but no longer focused"); - return; - } - if (text_input->relay->input_method == NULL) { - sway_log(SWAY_INFO, "Enabling text input when input method is gone"); - return; - } - wlr_input_method_v2_send_activate(text_input->relay->input_method); - relay_send_im_state(text_input->relay, text_input->input); -} - -static void handle_text_input_commit(struct wl_listener *listener, - void *data) { - struct sway_text_input *text_input = wl_container_of(listener, text_input, - text_input_commit); - if (text_input->input->focused_surface == NULL) { - sway_log(SWAY_DEBUG, "Unfocused text input tried to commit an update"); - return; - } - if (!text_input->input->current_enabled) { - sway_log(SWAY_INFO, "Inactive text input tried to commit an update"); - return; - } - sway_log(SWAY_DEBUG, "Text input committed update"); - if (text_input->relay->input_method == NULL) { - sway_log(SWAY_INFO, "Text input committed, but input method is gone"); - return; - } - relay_send_im_state(text_input->relay, text_input->input); -} - -static void relay_disable_text_input(struct sway_input_method_relay *relay, - struct sway_text_input *text_input) { - if (relay->input_method == NULL) { - sway_log(SWAY_DEBUG, "Disabling text input, but input method is gone"); - return; - } - wlr_input_method_v2_send_deactivate(relay->input_method); - relay_send_im_state(relay, text_input->input); -} - -static void handle_text_input_disable(struct wl_listener *listener, - void *data) { - struct sway_text_input *text_input = wl_container_of(listener, text_input, - text_input_disable); - if (text_input->input->focused_surface == NULL) { - sway_log(SWAY_DEBUG, "Disabling text input, but no longer focused"); - return; - } - relay_disable_text_input(text_input->relay, text_input); -} - -static void handle_text_input_destroy(struct wl_listener *listener, - void *data) { - struct sway_text_input *text_input = wl_container_of(listener, text_input, - text_input_destroy); - - if (text_input->input->current_enabled) { - relay_disable_text_input(text_input->relay, text_input); - } - text_input_set_pending_focused_surface(text_input, NULL); - wl_list_remove(&text_input->text_input_commit.link); - wl_list_remove(&text_input->text_input_destroy.link); - wl_list_remove(&text_input->text_input_disable.link); - wl_list_remove(&text_input->text_input_enable.link); - wl_list_remove(&text_input->link); - free(text_input); -} - -static void handle_pending_focused_surface_destroy(struct wl_listener *listener, - void *data) { - struct sway_text_input *text_input = wl_container_of(listener, text_input, - pending_focused_surface_destroy); - text_input->pending_focused_surface = NULL; - wl_list_remove(&text_input->pending_focused_surface_destroy.link); - wl_list_init(&text_input->pending_focused_surface_destroy.link); -} - -struct sway_text_input *sway_text_input_create( - struct sway_input_method_relay *relay, - struct wlr_text_input_v3 *text_input) { - struct sway_text_input *input = calloc(1, sizeof(struct sway_text_input)); - if (!input) { - return NULL; - } - input->input = text_input; - input->relay = relay; - - wl_list_insert(&relay->text_inputs, &input->link); - - input->text_input_enable.notify = handle_text_input_enable; - wl_signal_add(&text_input->events.enable, &input->text_input_enable); - - input->text_input_commit.notify = handle_text_input_commit; - wl_signal_add(&text_input->events.commit, &input->text_input_commit); - - input->text_input_disable.notify = handle_text_input_disable; - wl_signal_add(&text_input->events.disable, &input->text_input_disable); - - input->text_input_destroy.notify = handle_text_input_destroy; - wl_signal_add(&text_input->events.destroy, &input->text_input_destroy); - - input->pending_focused_surface_destroy.notify = - handle_pending_focused_surface_destroy; - wl_list_init(&input->pending_focused_surface_destroy.link); - return input; -} - -static void relay_handle_text_input(struct wl_listener *listener, - void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - text_input_new); - struct wlr_text_input_v3 *wlr_text_input = data; - if (relay->seat->wlr_seat != wlr_text_input->seat) { - return; - } - - sway_text_input_create(relay, wlr_text_input); -} - -static void input_popup_set_focus(struct sway_input_popup *popup, - struct wlr_surface *surface) { - wl_list_remove(&popup->focused_surface_unmap.link); - - if (!popup->scene_tree) { - wl_list_init(&popup->focused_surface_unmap.link); - return; - } - - if (popup->desc.relative) { - scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); - wlr_scene_node_destroy(popup->desc.relative); - popup->desc.relative = NULL; - } - - if (surface == NULL) { - wl_list_init(&popup->focused_surface_unmap.link); - wlr_scene_node_set_enabled(&popup->scene_tree->node, false); - return; - } - - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(surface); - struct wlr_session_lock_surface_v1 *lock_surface = - wlr_session_lock_surface_v1_try_from_wlr_surface(surface); - - struct wlr_scene_tree *relative_parent; - if (layer_surface) { - wl_signal_add(&layer_surface->surface->events.unmap, - &popup->focused_surface_unmap); - - struct sway_layer_surface *layer = layer_surface->data; - if (layer == NULL) { - return; - } - - relative_parent = layer->scene->tree; - popup->desc.view = NULL; - - // we don't need to add an event here to NULL out this field because - // this field will only be initialized if the popup is part of a layer - // surface. Layer surfaces get destroyed as part of the output being - // destroyed, thus also trickling down to popups. - popup->fixed_output = layer->layer_surface->output; - } else if (lock_surface) { - wl_signal_add(&lock_surface->surface->events.unmap, - &popup->focused_surface_unmap); - - struct sway_layer_surface *lock = lock_surface->data; - if (lock == NULL) { - return; - } - - relative_parent = lock->scene->tree; - popup->desc.view = NULL; - - // we don't need to add an event here to NULL out this field because - // this field will only be initialized if the popup is part of a layer - // surface. Layer surfaces get destroyed as part of the output being - // destroyed, thus also trickling down to popups. - popup->fixed_output = lock->layer_surface->output; - } else { - struct sway_view *view = view_from_wlr_surface(surface); - // In the future there may be other shells been added, so we also need to check here. - if (view == NULL) { - sway_log(SWAY_DEBUG, "Unsupported IME focus surface"); - return; - } - wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); - relative_parent = view->scene_tree; - popup->desc.view = view; - } - - struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent); - - popup->desc.relative = &relative->node; - if (!scene_descriptor_assign(&popup->scene_tree->node, - SWAY_SCENE_DESC_POPUP, &popup->desc)) { - wlr_scene_node_destroy(&popup->scene_tree->node); - popup->scene_tree = NULL; - return; - } - - constrain_popup(popup); - wlr_scene_node_set_enabled(&popup->scene_tree->node, true); -} - -static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_destroy); - wlr_scene_node_destroy(&popup->scene_tree->node); - wl_list_remove(&popup->focused_surface_unmap.link); - wl_list_remove(&popup->popup_surface_commit.link); - wl_list_remove(&popup->popup_surface_map.link); - wl_list_remove(&popup->popup_surface_unmap.link); - wl_list_remove(&popup->popup_destroy.link); - wl_list_remove(&popup->link); - - free(popup); -} - -static void handle_im_popup_surface_map(struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_surface_map); - struct sway_text_input *text_input = relay_get_focused_text_input(popup->relay); - if (text_input != NULL) { - input_popup_set_focus(popup, text_input->input->focused_surface); - } else { - input_popup_set_focus(popup, NULL); - } -} - -static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_surface_unmap); - - scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); - // relative should already be freed as it should be a child of the just unmapped scene - popup->desc.relative = NULL; - - input_popup_set_focus(popup, NULL); -} - -static void handle_im_popup_surface_commit(struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_surface_commit); - - constrain_popup(popup); -} - -static void handle_im_focused_surface_unmap( - struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, focused_surface_unmap); - - input_popup_set_focus(popup, NULL); -} - -static void handle_im_new_popup_surface(struct wl_listener *listener, - void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_new_popup_surface); - struct sway_input_popup *popup = calloc(1, sizeof(*popup)); - if (!popup) { - sway_log(SWAY_ERROR, "Failed to allocate an input method popup"); - return; - } - - popup->relay = relay; - popup->popup_surface = data; - popup->popup_surface->data = popup; - - popup->scene_tree = wlr_scene_tree_create(root->layers.popup); - if (!popup->scene_tree) { - sway_log(SWAY_ERROR, "Failed to allocate scene tree"); - free(popup); - return; - } - - if (!wlr_scene_subsurface_tree_create(popup->scene_tree, - popup->popup_surface->surface)) { - sway_log(SWAY_ERROR, "Failed to allocate subsurface tree"); - wlr_scene_node_destroy(&popup->scene_tree->node); - free(popup); - return; - } - - wl_signal_add(&popup->popup_surface->events.destroy, &popup->popup_destroy); - popup->popup_destroy.notify = handle_im_popup_destroy; - wl_signal_add(&popup->popup_surface->surface->events.commit, &popup->popup_surface_commit); - popup->popup_surface_commit.notify = handle_im_popup_surface_commit; - wl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_surface_map); - popup->popup_surface_map.notify = handle_im_popup_surface_map; - wl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->popup_surface_unmap); - popup->popup_surface_unmap.notify = handle_im_popup_surface_unmap; - wl_list_init(&popup->focused_surface_unmap.link); - popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; - - struct sway_text_input *text_input = relay_get_focused_text_input(relay); - if (text_input != NULL) { - input_popup_set_focus(popup, text_input->input->focused_surface); - } else { - input_popup_set_focus(popup, NULL); - } - - wl_list_insert(&relay->input_popups, &popup->link); -} - -static void text_input_send_enter(struct sway_text_input *text_input, - struct wlr_surface *surface) { - wlr_text_input_v3_send_enter(text_input->input, surface); - struct sway_input_popup *popup; - wl_list_for_each(popup, &text_input->relay->input_popups, link) { - input_popup_set_focus(popup, surface); - } -} - -static void relay_handle_input_method(struct wl_listener *listener, - void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_new); - struct wlr_input_method_v2 *input_method = data; - if (relay->seat->wlr_seat != input_method->seat) { - return; - } - - if (relay->input_method != NULL) { - sway_log(SWAY_INFO, "Attempted to connect second input method to a seat"); - wlr_input_method_v2_send_unavailable(input_method); - return; - } - - relay->input_method = input_method; - wl_signal_add(&relay->input_method->events.commit, - &relay->input_method_commit); - relay->input_method_commit.notify = handle_im_commit; - wl_signal_add(&relay->input_method->events.grab_keyboard, - &relay->input_method_grab_keyboard); - relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; - wl_signal_add(&relay->input_method->events.destroy, - &relay->input_method_destroy); - relay->input_method_destroy.notify = handle_im_destroy; - wl_signal_add(&relay->input_method->events.new_popup_surface, - &relay->input_method_new_popup_surface); - relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; - - struct sway_text_input *text_input = relay_get_focusable_text_input(relay); - if (text_input) { - text_input_send_enter(text_input, - text_input->pending_focused_surface); - text_input_set_pending_focused_surface(text_input, NULL); - } -} - -static void sway_input_method_relay_finish_text_input(struct sway_input_method_relay *relay) { - wl_list_remove(&relay->text_input_new.link); - wl_list_remove(&relay->text_input_manager_destroy.link); - wl_list_init(&relay->text_input_new.link); - wl_list_init(&relay->text_input_manager_destroy.link); -} - -static void relay_handle_text_input_manager_destroy(struct wl_listener *listener, void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - text_input_manager_destroy); - - sway_input_method_relay_finish_text_input(relay); -} - -static void sway_input_method_relay_finish_input_method(struct sway_input_method_relay *relay) { - wl_list_remove(&relay->input_method_new.link); - wl_list_remove(&relay->input_method_manager_destroy.link); - wl_list_init(&relay->input_method_new.link); - wl_list_init(&relay->input_method_manager_destroy.link); -} - -static void relay_handle_input_method_manager_destroy(struct wl_listener *listener, void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_manager_destroy); - - sway_input_method_relay_finish_input_method(relay); -} - -void sway_input_method_relay_init(struct sway_seat *seat, - struct sway_input_method_relay *relay) { - relay->seat = seat; - wl_list_init(&relay->text_inputs); - wl_list_init(&relay->input_popups); - - relay->text_input_new.notify = relay_handle_text_input; - wl_signal_add(&server.text_input->events.new_text_input, - &relay->text_input_new); - relay->text_input_manager_destroy.notify = relay_handle_text_input_manager_destroy; - wl_signal_add(&server.text_input->events.destroy, - &relay->text_input_manager_destroy); - - relay->input_method_new.notify = relay_handle_input_method; - wl_signal_add( - &server.input_method->events.new_input_method, - &relay->input_method_new); - relay->input_method_manager_destroy.notify = relay_handle_input_method_manager_destroy; - wl_signal_add(&server.input_method->events.destroy, - &relay->input_method_manager_destroy); -} - -void sway_input_method_relay_finish(struct sway_input_method_relay *relay) { - sway_input_method_relay_finish_text_input(relay); - sway_input_method_relay_finish_input_method(relay); -} - -void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay, - struct wlr_surface *surface) { - struct sway_text_input *text_input; - wl_list_for_each(text_input, &relay->text_inputs, link) { - if (text_input->pending_focused_surface) { - assert(text_input->input->focused_surface == NULL); - if (surface != text_input->pending_focused_surface) { - text_input_set_pending_focused_surface(text_input, NULL); - } - } else if (text_input->input->focused_surface) { - assert(text_input->pending_focused_surface == NULL); - if (surface != text_input->input->focused_surface) { - relay_disable_text_input(relay, text_input); - wlr_text_input_v3_send_leave(text_input->input); - } else { - sway_log(SWAY_DEBUG, "IM relay set_focus already focused"); - continue; - } - } - - if (surface - && wl_resource_get_client(text_input->input->resource) - == wl_resource_get_client(surface->resource)) { - if (relay->input_method) { - wlr_text_input_v3_send_enter(text_input->input, surface); - } else { - text_input_set_pending_focused_surface(text_input, surface); - } - } - } -} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 3b69ad384..23016dbd5 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -1,18 +1,11 @@ -#include -#include #include #include #include -#include -#include -#include -#include -#include +#include #include "config.h" #include "log.h" #include "sway/config.h" #include "sway/ipc-json.h" -#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -20,30 +13,15 @@ #include "sway/input/input-manager.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" -#include "wlr-layer-shell-unstable-v1-protocol.h" -#include "sway/desktop/idle_inhibit_v1.h" - -#if WLR_HAS_LIBINPUT_BACKEND #include -#endif +#include +#include +#include +#include "wlr-layer-shell-unstable-v1-protocol.h" static const int i3_output_id = INT32_MAX; static const int i3_scratch_id = INT32_MAX - 1; -static const char *ipc_json_node_type_description(enum sway_node_type node_type) { - switch (node_type) { - case N_ROOT: - return "root"; - case N_OUTPUT: - return "output"; - case N_WORKSPACE: - return "workspace"; - case N_CONTAINER: - return "con"; - } - return "none"; -} - static const char *ipc_json_layout_description(enum sway_container_layout l) { switch (l) { case L_VERT: @@ -90,143 +68,46 @@ static const char *ipc_json_output_transform_description(enum wl_output_transfor case WL_OUTPUT_TRANSFORM_NORMAL: return "normal"; case WL_OUTPUT_TRANSFORM_90: - // Sway uses clockwise transforms, while WL_OUTPUT_TRANSFORM_* describes - // anti-clockwise transforms. - return "270"; + return "90"; case WL_OUTPUT_TRANSFORM_180: return "180"; case WL_OUTPUT_TRANSFORM_270: - // Transform also inverted here. - return "90"; + return "270"; case WL_OUTPUT_TRANSFORM_FLIPPED: return "flipped"; case WL_OUTPUT_TRANSFORM_FLIPPED_90: - // Inverted. - return "flipped-270"; + return "flipped-90"; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return "flipped-180"; case WL_OUTPUT_TRANSFORM_FLIPPED_270: - // Inverted. - return "flipped-90"; + return "flipped-270"; } return NULL; } -static const char *ipc_json_output_adaptive_sync_status_description( - enum wlr_output_adaptive_sync_status status) { - switch (status) { - case WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED: - return "disabled"; - case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: - return "enabled"; +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 NULL; -} - -static const char *ipc_json_output_mode_aspect_ratio_description( - enum wlr_output_mode_aspect_ratio aspect_ratio) { - switch (aspect_ratio) { - case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE: - return "none"; - case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3: - return "4:3"; - case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9: - return "16:9"; - case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27: - return "64:27"; - case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135: - return "256:135"; - } - return NULL; -} - -static json_object *ipc_json_output_mode_description( - const struct wlr_output_mode *mode) { - const char *pic_ar = - ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio); - json_object *mode_object = json_object_new_object(); - json_object_object_add(mode_object, "width", - json_object_new_int(mode->width)); - json_object_object_add(mode_object, "height", - json_object_new_int(mode->height)); - json_object_object_add(mode_object, "refresh", - json_object_new_int(mode->refresh)); - json_object_object_add(mode_object, "picture_aspect_ratio", - json_object_new_string(pic_ar)); - return mode_object; -} - -#if WLR_HAS_XWAYLAND -static const char *ipc_json_xwindow_type_description(struct sway_view *view) { - struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - struct sway_xwayland *xwayland = &server.xwayland; - - for (size_t i = 0; i < surface->window_type_len; ++i) { - xcb_atom_t type = surface->window_type[i]; - if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_NORMAL]) { - return "normal"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_DIALOG]) { - return "dialog"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_UTILITY]) { - return "utility"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_TOOLBAR]) { - return "toolbar"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_SPLASH]) { - return "splash"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_MENU]) { - return "menu"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_DROPDOWN_MENU]) { - return "dropdown_menu"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_POPUP_MENU]) { - return "popup_menu"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_TOOLTIP]) { - return "tooltip"; - } else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_NOTIFICATION]) { - return "notification"; - } else { - return "unknown"; - } - } - return "unknown"; } -#endif - -static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhibit_mode mode) { - switch (mode) { - case INHIBIT_IDLE_FOCUS: - return "focus"; - case INHIBIT_IDLE_FULLSCREEN: - return "fullscreen"; - case INHIBIT_IDLE_OPEN: - return "open"; - case INHIBIT_IDLE_VISIBLE: - return "visible"; - case INHIBIT_IDLE_APPLICATION: - return NULL; - } - return NULL; -} - -static const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) { - switch (type) { - case WP_CONTENT_TYPE_V1_TYPE_NONE: - return "none"; - case WP_CONTENT_TYPE_V1_TYPE_PHOTO: - return "photo"; - case WP_CONTENT_TYPE_V1_TYPE_VIDEO: - return "video"; - case WP_CONTENT_TYPE_V1_TYPE_GAME: - return "game"; - } - return NULL; -} json_object *ipc_json_get_version(void) { int major = 0, minor = 0, patch = 0; json_object *version = json_object_new_object(); - sscanf(SWAY_VERSION, "%d.%d.%d", &major, &minor, &patch); + sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch); json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION)); json_object_object_add(version, "variant", json_object_new_string("sway")); @@ -255,22 +136,16 @@ static json_object *ipc_json_create_empty_rect(void) { return ipc_json_create_rect(&empty); } -static json_object *ipc_json_create_node(int id, const char* type, char *name, +static json_object *ipc_json_create_node(int id, char *name, bool focused, json_object *focus, struct wlr_box *box) { json_object *object = json_object_new_object(); json_object_object_add(object, "id", json_object_new_int(id)); - json_object_object_add(object, "type", json_object_new_string(type)); - json_object_object_add(object, "orientation", - json_object_new_string( - ipc_json_orientation_description(L_HORIZ))); - json_object_object_add(object, "percent", NULL); - json_object_object_add(object, "urgent", json_object_new_boolean(false)); - json_object_object_add(object, "marks", json_object_new_array()); + json_object_object_add(object, "name", + name ? json_object_new_string(name) : NULL); + json_object_object_add(object, "rect", ipc_json_create_rect(box)); json_object_object_add(object, "focused", json_object_new_boolean(focused)); - json_object_object_add(object, "layout", - json_object_new_string( - ipc_json_layout_description(L_HORIZ))); + json_object_object_add(object, "focus", focus); // set default values to be compatible with i3 json_object_object_add(object, "border", @@ -278,32 +153,53 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name, ipc_json_border_description(B_NONE))); json_object_object_add(object, "current_border_width", json_object_new_int(0)); - json_object_object_add(object, "rect", ipc_json_create_rect(box)); - json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); + json_object_object_add(object, "layout", + json_object_new_string( + ipc_json_layout_description(L_HORIZ))); + json_object_object_add(object, "orientation", + json_object_new_string( + ipc_json_orientation_description(L_HORIZ))); + json_object_object_add(object, "percent", NULL); json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); + json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); - json_object_object_add(object, "name", - name ? json_object_new_string(name) : NULL); json_object_object_add(object, "window", NULL); - json_object_object_add(object, "nodes", json_object_new_array()); + json_object_object_add(object, "urgent", json_object_new_boolean(false)); json_object_object_add(object, "floating_nodes", json_object_new_array()); - json_object_object_add(object, "focus", focus); - json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); json_object_object_add(object, "sticky", json_object_new_boolean(false)); - json_object_object_add(object, "floating", NULL); - json_object_object_add(object, "scratchpad_state", NULL); return object; } -static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { +static void ipc_json_describe_root(struct sway_root *root, json_object *object) { + json_object_object_add(object, "type", json_object_new_string("root")); +} + +static void ipc_json_describe_output(struct sway_output *output, + json_object *object) { + 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, "primary", json_object_new_boolean(false)); + json_object_object_add(object, "layout", json_object_new_string("output")); + json_object_object_add(object, "orientation", + json_object_new_string( + ipc_json_orientation_description(L_NONE))); json_object_object_add(object, "make", - json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); + json_object_new_string(wlr_output->make)); json_object_object_add(object, "model", - json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown")); + json_object_new_string(wlr_output->model)); json_object_object_add(object, "serial", - json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown")); + json_object_new_string(wlr_output->serial)); + json_object_object_add(object, "scale", + json_object_new_double(wlr_output->scale)); + json_object_object_add(object, "transform", + json_object_new_string( + ipc_json_output_transform_description(wlr_output->transform))); + + struct sway_workspace *ws = output_get_active_workspace(output); + json_object_object_add(object, "current_workspace", + json_object_new_string(ws->name)); json_object *modes_array = json_object_new_array(); struct wlr_output_mode *mode; @@ -317,81 +213,16 @@ static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_obj json_object_new_int(mode->refresh)); json_object_array_add(modes_array, mode_object); } - json_object_object_add(object, "modes", modes_array); - - json_object *features_object = json_object_new_object(); - json_object_object_add(features_object, "adaptive_sync", - json_object_new_boolean(wlr_output->adaptive_sync_supported || - wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED)); - json_object_object_add(features_object, "hdr", - json_object_new_boolean(output_supports_hdr(wlr_output, NULL))); - json_object_object_add(object, "features", features_object); -} - -static void ipc_json_describe_output(struct sway_output *output, - json_object *object) { - ipc_json_describe_wlr_output(output->wlr_output, object); -} - -static void ipc_json_describe_enabled_output(struct sway_output *output, - json_object *object) { - ipc_json_describe_output(output, object); - - struct wlr_output *wlr_output = output->wlr_output; - json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); - json_object_object_add(object, "active", json_object_new_boolean(true)); - json_object_object_add(object, "dpms", - json_object_new_boolean(wlr_output->enabled)); - json_object_object_add(object, "power", - json_object_new_boolean(wlr_output->enabled)); - json_object_object_add(object, "layout", json_object_new_string("output")); - json_object_object_add(object, "orientation", - json_object_new_string( - ipc_json_orientation_description(L_NONE))); - json_object_object_add(object, "scale", - json_object_new_double(wlr_output->scale)); - json_object_object_add(object, "scale_filter", - json_object_new_string( - sway_output_scale_filter_to_string(output->scale_filter))); - json_object_object_add(object, "transform", - json_object_new_string( - ipc_json_output_transform_description(wlr_output->transform))); - const char *adaptive_sync_status = - ipc_json_output_adaptive_sync_status_description( - wlr_output->adaptive_sync_status); - json_object_object_add(object, "adaptive_sync_status", - json_object_new_string(adaptive_sync_status)); - - struct sway_workspace *ws = output_get_active_workspace(output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return; - } - json_object_object_add(object, "current_workspace", - json_object_new_string(ws->name)); - - 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 = - ipc_json_output_mode_description(mode); - json_object_array_add(modes_array, mode_object); - } json_object_object_add(object, "modes", modes_array); - json_object *current_mode_object; - if (wlr_output->current_mode != NULL) { - current_mode_object = - ipc_json_output_mode_description(wlr_output->current_mode); - } else { - current_mode_object = json_object_new_object(); - json_object_object_add(current_mode_object, "width", - json_object_new_int(wlr_output->width)); - json_object_object_add(current_mode_object, "height", - json_object_new_int(wlr_output->height)); - json_object_object_add(current_mode_object, "refresh", - json_object_new_int(wlr_output->refresh)); - } + json_object *current_mode_object = json_object_new_object(); + json_object_object_add(current_mode_object, "width", + json_object_new_int(wlr_output->width)); + json_object_object_add(current_mode_object, "height", + json_object_new_int(wlr_output->height)); + json_object_object_add(current_mode_object, "refresh", + json_object_new_int(wlr_output->refresh)); json_object_object_add(object, "current_mode", current_mode_object); struct sway_node *parent = node_get_parent(&output->node); @@ -406,10 +237,6 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, * ((double)output->height / parent_box.height); json_object_object_add(object, "percent", json_object_new_double(percent)); } - - json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time)); - json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing)); - json_object_object_add(object, "hdr", json_object_new_boolean(output->hdr)); } json_object *ipc_json_describe_disabled_output(struct sway_output *output) { @@ -417,45 +244,23 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object *object = json_object_new_object(); - ipc_json_describe_output(output, object); - - json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); json_object_object_add(object, "type", json_object_new_string("output")); json_object_object_add(object, "name", json_object_new_string(wlr_output->name)); json_object_object_add(object, "active", json_object_new_boolean(false)); - json_object_object_add(object, "dpms", json_object_new_boolean(false)); - json_object_object_add(object, "power", json_object_new_boolean(false)); - - 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, "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_object_add(object, "percent", NULL); return object; } -json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) { - struct wlr_output *wlr_output = output->wlr_output; - - json_object *object = json_object_new_object(); - - ipc_json_describe_wlr_output(wlr_output, object); - - json_object_object_add(object, "non_desktop", json_object_new_boolean(true)); - json_object_object_add(object, "type", json_object_new_string("output")); - json_object_object_add(object, "name", - json_object_new_string(wlr_output->name)); - - return object; -} - static json_object *ipc_json_describe_scratchpad_output(void) { struct wlr_box box; root_get_box(root, &box); @@ -468,9 +273,10 @@ static json_object *ipc_json_describe_scratchpad_output(void) { json_object_new_int(container->node.id)); } - json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace", + json_object *workspace = ipc_json_create_node(i3_scratch_id, "__i3_scratch", false, workspace_focus, &box); - json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); + json_object_object_add(workspace, "type", + json_object_new_string("workspace")); // List all hidden scratchpad containers as floating nodes json_object *floating_array = json_object_new_array(); @@ -487,8 +293,10 @@ static json_object *ipc_json_describe_scratchpad_output(void) { json_object *output_focus = json_object_new_array(); json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); - json_object *output = ipc_json_create_node(i3_output_id, "output", + json_object *output = ipc_json_create_node(i3_output_id, "__i3", false, output_focus, &box); + json_object_object_add(output, "type", + json_object_new_string("output")); json_object_object_add(output, "layout", json_object_new_string("output")); @@ -501,23 +309,12 @@ static json_object *ipc_json_describe_scratchpad_output(void) { static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object *object) { - int num; - if (isdigit(workspace->name[0])) { - errno = 0; - char *endptr = NULL; - long long parsed_num = strtoll(workspace->name, &endptr, 10); - if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == workspace->name) { - num = -1; - } else { - num = (int) parsed_num; - } - } else { - num = -1; - } + int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; + json_object_object_add(object, "num", json_object_new_int(num)); - json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); json_object_object_add(object, "output", workspace->output ? json_object_new_string(workspace->output->wlr_output->name) : NULL); + json_object_object_add(object, "type", json_object_new_string("workspace")); json_object_object_add(object, "urgent", json_object_new_boolean(workspace->urgent)); json_object_object_add(object, "representation", workspace->representation ? @@ -540,45 +337,6 @@ 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); - list_t *siblings = container_get_siblings(c); - bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED) - && ((siblings && siblings->length > 1) || !config->hide_lone_tab); - if (((!tab_or_stack || container_is_floating(c)) && - c->current.border != B_NORMAL) || - c->pending.fullscreen_mode != FULLSCREEN_NONE || - c->pending.workspace == NULL) { - deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; - return; - } - - if (c->pending.parent) { - deco_rect->x = c->pending.x - c->pending.parent->pending.x; - deco_rect->y = c->pending.y - c->pending.parent->pending.y; - } else { - deco_rect->x = c->pending.x - c->pending.workspace->x; - deco_rect->y = c->pending.y - c->pending.workspace->y; - } - deco_rect->width = c->pending.width; - deco_rect->height = container_titlebar_height(); - - if (!container_is_floating(c)) { - if (parent_layout == L_TABBED) { - deco_rect->width = c->pending.parent - ? c->pending.parent->pending.width / c->pending.parent->pending.children->length - : c->pending.workspace->width / c->pending.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)); @@ -586,88 +344,36 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "app_id", app_id ? json_object_new_string(app_id) : NULL); - json_object_object_add(object, "foreign_toplevel_identifier", - c->view->ext_foreign_toplevel ? - json_object_new_string(c->view->ext_foreign_toplevel->identifier) : NULL); + json_object *marks = json_object_new_array(); + list_t *con_marks = c->marks; + for (int i = 0; i < con_marks->length; ++i) { + json_object_array_add(marks, json_object_new_string(con_marks->items[i])); + } - bool visible = view_is_visible(c->view); - json_object_object_add(object, "visible", json_object_new_boolean(visible)); + json_object_object_add(object, "marks", marks); - bool has_titlebar = c->title_bar.tree->node.enabled; struct wlr_box window_box = { - c->pending.content_x - c->pending.x, - has_titlebar ? 0 : c->pending.content_y - c->pending.y, - c->pending.content_width, - c->pending.content_height + c->content_x - c->x, + (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, + c->content_width, + c->content_height }; 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)); - json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time)); - - json_object_object_add(object, "allow_tearing", json_object_new_boolean(view_can_tear(c->view))); - - json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view))); - - json_object_object_add(object, "inhibit_idle", - json_object_new_boolean(view_inhibit_idle(c->view))); - - const char *sandbox_engine = view_get_sandbox_engine(c->view); - json_object_object_add(object, "sandbox_engine", - sandbox_engine ? json_object_new_string(sandbox_engine) : NULL); - - const char *sandbox_app_id = view_get_sandbox_app_id(c->view); - json_object_object_add(object, "sandbox_app_id", - sandbox_app_id ? json_object_new_string(sandbox_app_id) : NULL); - - const char *sandbox_instance_id = view_get_sandbox_instance_id(c->view); - json_object_object_add(object, "sandbox_instance_id", - sandbox_instance_id ? json_object_new_string(sandbox_instance_id) : NULL); - - const char *tag = view_get_tag(c->view); - json_object_object_add(object, "tag", tag ? json_object_new_string(tag) : NULL); - - json_object *idle_inhibitors = json_object_new_object(); - - struct sway_idle_inhibitor_v1 *user_inhibitor = - sway_idle_inhibit_v1_user_inhibitor_for_view(c->view); - - if (user_inhibitor) { - json_object_object_add(idle_inhibitors, "user", - json_object_new_string( - ipc_json_user_idle_inhibitor_description(user_inhibitor->mode))); - } else { - json_object_object_add(idle_inhibitors, "user", - json_object_new_string("none")); - } - - struct sway_idle_inhibitor_v1 *application_inhibitor = - sway_idle_inhibit_v1_application_inhibitor_for_view(c->view); - - if (application_inhibitor) { - json_object_object_add(idle_inhibitors, "application", - json_object_new_string("enabled")); - } else { - json_object_object_add(idle_inhibitors, "application", - json_object_new_string("none")); - } - - json_object_object_add(object, "idle_inhibitors", idle_inhibitors); - - enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE; - if (c->view->surface != NULL) { - content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1, - c->view->surface); - } - if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) { - json_object_object_add(object, "content_type", - json_object_new_string(ipc_json_content_type_description(content_type))); - } - -#if WLR_HAS_XWAYLAND +#if HAVE_XWAYLAND if (c->view->type == SWAY_VIEW_XWAYLAND) { json_object_object_add(object, "window", json_object_new_int(view_get_x11_window_id(c->view))); @@ -696,13 +402,6 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(window_props, "window_role", json_object_new_string(role)); } - uint32_t window_type = view_get_window_type(c->view); - if (window_type) { - json_object_object_add(window_props, "window_type", - json_object_new_string( - ipc_json_xwindow_type_description(c->view))); - } - json_object_object_add(object, "window_properties", window_props); } #endif @@ -711,35 +410,24 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object static void ipc_json_describe_container(struct sway_container *c, json_object *object) { json_object_object_add(object, "name", c->title ? json_object_new_string(c->title) : NULL); - bool floating = container_is_floating(c); - if (floating) { - json_object_object_add(object, "type", - json_object_new_string("floating_con")); - } + json_object_object_add(object, "type", + json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); json_object_object_add(object, "layout", json_object_new_string( - ipc_json_layout_description(c->pending.layout))); + ipc_json_layout_description(c->layout))); json_object_object_add(object, "orientation", json_object_new_string( - ipc_json_orientation_description(c->pending.layout))); + ipc_json_orientation_description(c->layout))); bool urgent = c->view ? view_is_urgent(c->view) : container_has_urgent_child(c); json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); - // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off" - json_object_object_add(object, "floating", - json_object_new_string(floating ? "user_on" : "auto_off")); - json_object_object_add(object, "fullscreen_mode", - json_object_new_int(c->pending.fullscreen_mode)); - - // sway doesn't track if window was resized in scratchpad, so we can't use "changed" - json_object_object_add(object, "scratchpad_state", - json_object_new_string(!c->scratchpad ? "none" : "fresh")); + json_object_new_int(c->fullscreen_mode)); struct sway_node *parent = node_get_parent(&c->node); struct wlr_box parent_box = {0, 0, 0, 0}; @@ -749,8 +437,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o } if (parent_box.width != 0 && parent_box.height != 0) { - double percent = ((double)c->pending.width / parent_box.width) - * ((double)c->pending.height / parent_box.height); + double percent = ((double)c->width / parent_box.width) + * ((double)c->height / parent_box.height); json_object_object_add(object, "percent", json_object_new_double(percent)); } @@ -761,18 +449,6 @@ 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)); - - json_object *marks = json_object_new_array(); - list_t *con_marks = c->marks; - for (int i = 0; i < con_marks->length; ++i) { - json_object_array_add(marks, json_object_new_string(con_marks->items[i])); - } - - json_object_object_add(object, "marks", marks); - if (c->view) { ipc_json_describe_view(c, object); } @@ -813,16 +489,6 @@ 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 = { @@ -831,14 +497,15 @@ json_object *ipc_json_describe_node(struct sway_node *node) { }; seat_for_each_node(seat, focus_inactive_children_iterator, &data); - json_object *object = ipc_json_create_node((int)node->id, - ipc_json_node_type_description(node->type), name, focused, focus, &box); + json_object *object = ipc_json_create_node( + (int)node->id, name, focused, focus, &box); switch (node->type) { case N_ROOT: + ipc_json_describe_root(root, object); break; case N_OUTPUT: - ipc_json_describe_enabled_output(node->sway_output, object); + ipc_json_describe_output(node->sway_output, object); break; case N_CONTAINER: ipc_json_describe_container(node->sway_container, object); @@ -881,10 +548,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { } break; case N_CONTAINER: - if (node->sway_container->pending.children) { - for (i = 0; i < node->sway_container->pending.children->length; ++i) { + if (node->sway_container->children) { + for (i = 0; i < node->sway_container->children->length; ++i) { struct sway_container *child = - node->sway_container->pending.children->items[i]; + node->sway_container->children->items[i]; json_object_array_add(children, ipc_json_describe_node_recursive(&child->node)); } @@ -896,247 +563,6 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { return object; } -#if WLR_HAS_LIBINPUT_BACKEND -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; -#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY: - drag_lock = "enabled_sticky"; - break; -#endif - } - 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; -#if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM - case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM: - accel_profile = "custom"; - break; -#endif - } - 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)); - - const char *button_map = "unknown"; - switch (libinput_device_config_click_get_clickfinger_button_map(device)) { - case LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM: - button_map = "lrm"; - break; - case LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR: - button_map = "lmr"; - break; - } - json_object_object_add(object, "clickfinger_button_map", - json_object_new_string(button_map)); - } - - 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)); - const char *lock = "unknown"; - switch (libinput_device_config_scroll_get_button_lock(device)) { - case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED: - lock = "enabled"; - break; - case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED: - lock = "disabled"; - break; - } - json_object_object_add(object, "scroll_button_lock", - json_object_new_string(lock)); - } - } - - 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)); - } - - if (libinput_device_config_dwtp_is_available(device)) { - const char *dwtp = "unknown"; - switch (libinput_device_config_dwtp_get_enabled(device)) { - case LIBINPUT_CONFIG_DWTP_ENABLED: - dwtp = "enabled"; - break; - case LIBINPUT_CONFIG_DWTP_DISABLED: - dwtp = "disabled"; - break; - } - json_object_object_add(object, "dwtp", json_object_new_string(dwtp)); - } - - if (libinput_device_config_calibration_has_matrix(device)) { - float matrix[6]; - libinput_device_config_calibration_get_matrix(device, matrix); - struct json_object* array = json_object_new_array(); - struct json_object* x; - for (int i = 0; i < 6; i++) { - x = json_object_new_double(matrix[i]); - json_object_array_add(array, x); - } - json_object_object_add(object, "calibration_matrix", array); - } - - return object; -} -#endif - json_object *ipc_json_describe_input(struct sway_input_device *device) { if (!(sway_assert(device, "Device must not be null"))) { return NULL; @@ -1148,67 +574,54 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_string(device->identifier)); json_object_object_add(object, "name", json_object_new_string(device->wlr_device->name)); + json_object_object_add(object, "vendor", + json_object_new_int(device->wlr_device->vendor)); + json_object_object_add(object, "product", + json_object_new_int(device->wlr_device->product)); json_object_object_add(object, "type", json_object_new_string( - input_device_get_type(device))); + ipc_json_device_type_description(device))); if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { - struct wlr_keyboard *keyboard = - wlr_keyboard_from_input_device(device->wlr_device); + struct wlr_keyboard *keyboard = device->wlr_device->keyboard; struct xkb_keymap *keymap = keyboard->keymap; struct xkb_state *state = keyboard->xkb_state; - - json_object_object_add(object, "repeat_delay", - json_object_new_int(keyboard->repeat_info.delay)); - json_object_object_add(object, "repeat_rate", - json_object_new_int(keyboard->repeat_info.rate)); - - json_object *layouts_arr = json_object_new_array(); - json_object_object_add(object, "xkb_layout_names", layouts_arr); - - xkb_layout_index_t num_layouts = - keymap ? xkb_keymap_num_layouts(keymap) : 0; - // Virtual keyboards might have null keymap + xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap); xkb_layout_index_t layout_idx; for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) { - const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx); - json_object_array_add(layouts_arr, - layout ? json_object_new_string(layout) : NULL); - - bool is_active = xkb_state_layout_index_is_active(state, - layout_idx, XKB_STATE_LAYOUT_EFFECTIVE); + bool is_active = + xkb_state_layout_index_is_active(state, + layout_idx, + XKB_STATE_LAYOUT_EFFECTIVE); if (is_active) { - json_object_object_add(object, "xkb_active_layout_index", - json_object_new_int(layout_idx)); + const char *layout = + xkb_keymap_layout_get_name(keymap, layout_idx); json_object_object_add(object, "xkb_active_layout_name", - layout ? json_object_new_string(layout) : NULL); + layout ? json_object_new_string(layout) : NULL); + break; } } } - if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { - struct input_config *ic = input_device_get_config(device); - float scroll_factor = 1.0f; - if (ic != NULL && !isnan(ic->scroll_factor) && - ic->scroll_factor != FLT_MIN) { - scroll_factor = ic->scroll_factor; - } - json_object_object_add(object, "scroll_factor", - json_object_new_double(scroll_factor)); - } - -#if WLR_HAS_LIBINPUT_BACKEND if (wlr_input_device_is_libinput(device->wlr_device)) { struct libinput_device *libinput_dev; libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); - json_object_object_add(object, "libinput", - describe_libinput_device(libinput_dev)); - json_object_object_add(object, "vendor", - json_object_new_int(libinput_device_get_id_vendor(libinput_dev))); - json_object_object_add(object, "product", - json_object_new_int(libinput_device_get_id_product(libinput_dev))); + + 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)); } -#endif return object; } @@ -1309,16 +722,12 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { json_object_new_boolean(bar->strip_workspace_numbers)); json_object_object_add(json, "strip_workspace_name", json_object_new_boolean(bar->strip_workspace_name)); - json_object_object_add(json, "workspace_min_width", - json_object_new_int(bar->workspace_min_width)); json_object_object_add(json, "binding_mode_indicator", json_object_new_boolean(bar->binding_mode_indicator)); json_object_object_add(json, "verbose", json_object_new_boolean(bar->verbose)); json_object_object_add(json, "pango_markup", - json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT - ? config->pango_markup - : bar->pango_markup)); + json_object_new_boolean(bar->pango_markup)); json_object *colors = json_object_new_object(); json_object_object_add(colors, "background", @@ -1472,10 +881,3 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { #endif return json; } - -json_object *ipc_json_get_binding_mode(void) { - json_object *current_mode = json_object_new_object(); - json_object_object_add(current_mode, "name", - json_object_new_string(config->current_mode->name)); - return current_mode; -} diff --git a/sway/ipc-server.c b/sway/ipc-server.c index b934bb568..df57cba51 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -1,4 +1,5 @@ // See https://i3wm.org/docs/ipc.html for protocol information +#define _POSIX_C_SOURCE 200112L #include #include #include @@ -13,7 +14,7 @@ #include #include #include -#include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/desktop/transaction.h" @@ -46,23 +47,22 @@ 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); 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, 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); +void ipc_client_handle_command(struct ipc_client *client); +bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); static void handle_display_destroy(struct wl_listener *listener, void *data) { if (ipc_event_source) { @@ -138,7 +138,7 @@ struct sockaddr_un *ipc_user_sockaddr(void) { dir = "/tmp"; } if (path_size <= snprintf(ipc_sockaddr->sun_path, path_size, - "%s/sway-ipc.%u.%i.sock", dir, getuid(), getpid())) { + "%s/sway-ipc.%i.%i.sock", dir, getuid(), getpid())) { sway_abort("Socket path won't fit into ipc_sockaddr->sun_path"); } @@ -148,6 +148,7 @@ struct sockaddr_un *ipc_user_sockaddr(void) { int ipc_handle_connection(int fd, uint32_t mask, void *data) { (void) fd; struct sway_server *server = data; + sway_log(SWAY_DEBUG, "Event on IPC listening socket"); assert(mask == WL_EVENT_READABLE); int client_fd = accept(ipc_socket, NULL, NULL); @@ -177,7 +178,7 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { return 0; } client->server = server; - client->pending_length = 0; + client->payload_length = 0; client->fd = client_fd; client->subscribed_events = 0; client->event_source = wl_event_loop_add_fd(server->wl_event_loop, @@ -208,10 +209,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { } if (mask & WL_EVENT_HANGUP) { + sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } + sway_log(SWAY_DEBUG, "Client %d readable", client->fd); + int read_available; if (ioctl(client_fd, FIONREAD, &read_available) == -1) { sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); @@ -220,13 +224,9 @@ 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->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); + if (client->payload_length > 0) { + if ((uint32_t)read_available >= client->payload_length) { + ipc_client_handle_command(client); } return 0; } @@ -236,6 +236,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { } uint8_t buf[IPC_HEADER_SIZE]; + uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic)); // Should be fully available, because read_available >= IPC_HEADER_SIZE ssize_t received = recv(client_fd, buf, IPC_HEADER_SIZE, 0); if (received == -1) { @@ -250,15 +251,11 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { return 0; } - memcpy(&client->pending_length, buf + sizeof(ipc_magic), sizeof(uint32_t)); - memcpy(&client->pending_type, buf + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t)); + memcpy(&client->payload_length, &buf32[0], sizeof(buf32[0])); + memcpy(&client->current_command, &buf32[1], sizeof(buf32[1])); - 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); + if (read_available - received >= (long)client->payload_length) { + ipc_client_handle_command(client); } return 0; @@ -281,8 +278,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) if ((client->subscribed_events & event_mask(event)) == 0) { continue; } - if (!ipc_send_reply(client, event, json_string, - (uint32_t)strlen(json_string))) { + client->current_command = event; + if (!ipc_send_reply(client, 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 @@ -413,8 +410,7 @@ void ipc_event_binding(struct sway_binding *binding) { json_object *symbols = json_object_new_array(); json_object *symbol = NULL; - switch (binding->type) { - case BINDING_KEYCODE:; // bindcode: populate input_codes + if (binding->type == BINDING_KEYCODE) { // bindcode: populate input_codes uint32_t keycode; for (int i = 0; i < binding->keys->length; ++i) { keycode = *(uint32_t *)binding->keys->items[i]; @@ -423,11 +419,7 @@ void ipc_event_binding(struct sway_binding *binding) { input_code = keycode; } } - break; - - case BINDING_KEYSYM: - case BINDING_MOUSESYM: - case BINDING_MOUSECODE:; // bindsym/mouse: populate symbols + } else { // bindsym/mouse: populate symbols uint32_t keysym; char buffer[64]; for (int i = 0; i < binding->keys->length; ++i) { @@ -448,14 +440,6 @@ void ipc_event_binding(struct sway_binding *binding) { json_object_array_add(symbols, str); } } - break; - - default: - sway_log(SWAY_DEBUG, "Unsupported ipc binding event"); - json_object_put(input_codes); - json_object_put(symbols); - json_object_put(json_binding); - return; // do not send any event } json_object_object_add(json_binding, "input_codes", input_codes); @@ -492,35 +476,6 @@ static void ipc_event_tick(const char *payload) { json_object_put(json); } -void ipc_event_input(const char *change, struct sway_input_device *device) { - if (!ipc_has_event_listeners(IPC_EVENT_INPUT)) { - return; - } - sway_log(SWAY_DEBUG, "Sending input event"); - - json_object *json = json_object_new_object(); - json_object_object_add(json, "change", json_object_new_string(change)); - json_object_object_add(json, "input", ipc_json_describe_input(device)); - - const char *json_string = json_object_to_json_string(json); - ipc_send_event(json_string, IPC_EVENT_INPUT); - json_object_put(json); -} - -void ipc_event_output(void) { - if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) { - return; - } - sway_log(SWAY_DEBUG, "Sending output event"); - - json_object *json = json_object_new_object(); - json_object_object_add(json, "change", json_object_new_string("unspecified")); - - const char *json_string = json_object_to_json_string(json); - ipc_send_event(json_string, IPC_EVENT_OUTPUT); - json_object_put(json); -} - int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; @@ -531,6 +486,7 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { } if (mask & WL_EVENT_HANGUP) { + sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } @@ -539,6 +495,8 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { return 0; } + sway_log(SWAY_DEBUG, "Client %d writable", client->fd); + ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); if (written == -1 && errno == EAGAIN) { @@ -609,21 +567,20 @@ static void ipc_get_marks_callback(struct sway_container *con, void *data) { } } -void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, - enum ipc_command_type payload_type) { +void ipc_client_handle_command(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; } - char *buf = malloc(payload_length + 1); + char *buf = malloc(client->payload_length + 1); if (!buf) { sway_log_errno(SWAY_INFO, "Unable to allocate IPC payload"); ipc_client_disconnect(client); return; } - if (payload_length > 0) { + if (client->payload_length > 0) { // Payload should be fully available - ssize_t received = recv(client->fd, buf, payload_length, 0); + ssize_t received = recv(client->fd, buf, client->payload_length, 0); if (received == -1) { sway_log_errno(SWAY_INFO, "Unable to receive payload from IPC client"); @@ -632,15 +589,16 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt return; } } - buf[payload_length] = '\0'; + buf[client->payload_length] = '\0'; - switch (payload_type) { + bool client_valid = true; + switch (client->current_command) { case IPC_COMMAND: { char *line = strtok(buf, "\n"); while (line) { size_t line_length = strlen(line); - if (line + line_length >= buf + payload_length) { + if (line + line_length >= buf + client->payload_length) { break; } line[line_length] = ';'; @@ -648,16 +606,10 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt } list_t *res_list = execute_command(buf, NULL, NULL); - if (modeset_is_pending()) { - // IPC expects commands to have taken immediate effect, so we need - // to force a modeset after output commands. We do a single modeset - // here to avoid modesetting for every output command in sequence. - force_modeset(); - } transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); int length = strlen(json); - ipc_send_reply(client, payload_type, json, (uint32_t)length); + client_valid = ipc_send_reply(client, json, (uint32_t)length); free(json); while (res_list->length) { struct cmd_results *results = res_list->items[0]; @@ -671,7 +623,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt case IPC_SEND_TICK: { ipc_event_tick(buf); - ipc_send_reply(client, payload_type, "{\"success\": true}", 17); + ipc_send_reply(client, "{\"success\": true}", 17); goto exit_cleanup; } @@ -692,26 +644,18 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt 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; wl_list_for_each(output, &root->all_outputs, link) { - if (!output->enabled && output != root->fallback_output) { + if (!output->enabled && output != root->noop_output) { json_object_array_add(outputs, ipc_json_describe_disabled_output(output)); } } - - for (int i = 0; i < root->non_desktop_outputs->length; i++) { - struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i]; - json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output)); - } - const char *json_string = json_object_to_json_string(outputs); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(outputs); // free goto exit_cleanup; } @@ -721,8 +665,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt 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); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(workspaces); // free goto exit_cleanup; } @@ -733,7 +677,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt struct json_object *request = json_tokener_parse(buf); if (request == NULL || !json_object_is_type(request, json_type_array)) { const char msg[] = "{\"success\": false}"; - ipc_send_reply(client, payload_type, msg, strlen(msg)); + client_valid = ipc_send_reply(client, msg, strlen(msg)); sway_log(SWAY_INFO, "Failed to parse subscribe request"); goto exit_cleanup; } @@ -744,8 +688,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); if (strcmp(event_type, "workspace") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); - } else if (strcmp(event_type, "output") == 0) { - client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT); } else if (strcmp(event_type, "barconfig_update") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); } else if (strcmp(event_type, "bar_state_update") == 0) { @@ -761,11 +703,9 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt } else if (strcmp(event_type, "tick") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_TICK); is_tick = true; - } else if (strcmp(event_type, "input") == 0) { - client->subscribed_events |= event_mask(IPC_EVENT_INPUT); } else { const char msg[] = "{\"success\": false}"; - ipc_send_reply(client, payload_type, msg, strlen(msg)); + client_valid = ipc_send_reply(client, msg, strlen(msg)); json_object_put(request); sway_log(SWAY_INFO, "Unsupported event type in subscribe request"); goto exit_cleanup; @@ -774,11 +714,11 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt json_object_put(request); const char msg[] = "{\"success\": true}"; - ipc_send_reply(client, payload_type, msg, strlen(msg)); + client_valid = ipc_send_reply(client, msg, strlen(msg)); if (is_tick) { + client->current_command = IPC_EVENT_TICK; const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}"; - ipc_send_reply(client, IPC_EVENT_TICK, tickmsg, - strlen(tickmsg)); + ipc_send_reply(client, tickmsg, strlen(tickmsg)); } goto exit_cleanup; } @@ -791,8 +731,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt json_object_array_add(inputs, ipc_json_describe_input(device)); } const char *json_string = json_object_to_json_string(inputs); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(inputs); // free goto exit_cleanup; } @@ -805,8 +745,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt json_object_array_add(seats, ipc_json_describe_seat(seat)); } const char *json_string = json_object_to_json_string(seats); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(seats); // free goto exit_cleanup; } @@ -815,8 +755,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt { json_object *tree = ipc_json_describe_node_recursive(&root->node); const char *json_string = json_object_to_json_string(tree); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(tree); goto exit_cleanup; } @@ -826,8 +766,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt 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); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(marks); goto exit_cleanup; } @@ -836,8 +776,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt { json_object *version = ipc_json_get_version(); const char *json_string = json_object_to_json_string(version); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(version); // free goto exit_cleanup; } @@ -852,8 +792,9 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt json_object_array_add(bars, json_object_new_string(bar->id)); } const char *json_string = json_object_to_json_string(bars); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, + (uint32_t)strlen(json_string)); json_object_put(bars); // free } else { // Send particular bar's details @@ -867,14 +808,15 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt } if (!bar) { const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; - ipc_send_reply(client, payload_type, error, - (uint32_t)strlen(error)); + client_valid = + ipc_send_reply(client, 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); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free } goto exit_cleanup; @@ -888,59 +830,53 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt json_object_array_add(modes, json_object_new_string(mode->name)); } const char *json_string = json_object_to_json_string(modes); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(modes); // free goto exit_cleanup; } - case IPC_GET_BINDING_STATE: - { - json_object *current_mode = ipc_json_get_binding_mode(); - const char *json_string = json_object_to_json_string(current_mode); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); - json_object_put(current_mode); // free - goto exit_cleanup; - } - case IPC_GET_CONFIG: { 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); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, 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, payload_type, msg, strlen(msg)); + ipc_send_reply(client, msg, strlen(msg)); goto exit_cleanup; } default: - sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type); + sway_log(SWAY_INFO, "Unknown IPC command type %i", client->current_command); goto exit_cleanup; } exit_cleanup: + if (client_valid) { + client->payload_length = 0; + } free(buf); + return; } -bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, - const char *payload, uint32_t payload_length) { +bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) { assert(payload); char data[IPC_HEADER_SIZE]; + uint32_t *data32 = (uint32_t*)(data + sizeof(ipc_magic)); memcpy(data, ipc_magic, sizeof(ipc_magic)); - memcpy(data + sizeof(ipc_magic), &payload_length, sizeof(payload_length)); - memcpy(data + sizeof(ipc_magic) + sizeof(payload_length), &payload_type, sizeof(payload_type)); + memcpy(&data32[0], &payload_length, sizeof(payload_length)); + memcpy(&data32[1], &client->current_command, sizeof(client->current_command)); while (client->write_buffer_len + IPC_HEADER_SIZE + payload_length >= client->write_buffer_size) { @@ -948,8 +884,7 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ } if (client->write_buffer_size > 4e6) { // 4 MB - sway_log(SWAY_ERROR, "Client write buffer too big (%zu), disconnecting client", - client->write_buffer_size); + sway_log(SWAY_ERROR, "Client write buffer too big, disconnecting client"); ipc_client_disconnect(client); return false; } @@ -973,5 +908,6 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ ipc_client_handle_writable, client); } + sway_log(SWAY_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); return true; } diff --git a/sway/lock.c b/sway/lock.c deleted file mode 100644 index b1df65718..000000000 --- a/sway/lock.c +++ /dev/null @@ -1,361 +0,0 @@ -#include -#include -#include -#include "log.h" -#include "sway/input/cursor.h" -#include "sway/input/keyboard.h" -#include "sway/input/seat.h" -#include "sway/layers.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/lock.h" - -struct sway_session_lock_output { - struct wlr_scene_tree *tree; - struct wlr_scene_rect *background; - struct sway_session_lock *lock; - - struct sway_output *output; - - struct wl_list link; // sway_session_lock::outputs - - struct wl_listener destroy; - - struct wlr_session_lock_surface_v1 *surface; - - // invalid if surface is NULL - struct wl_listener surface_destroy; - struct wl_listener surface_map; -}; - -static void focus_surface(struct sway_session_lock *lock, - struct wlr_surface *focused) { - lock->focused = focused; - - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seat_set_focus_surface(seat, focused, false); - } -} - -static void refocus_output(struct sway_session_lock_output *output) { - // Move the seat focus to another surface if one is available - if (output->lock->focused == output->surface->surface) { - struct wlr_surface *next_focus = NULL; - - struct sway_session_lock_output *candidate; - wl_list_for_each(candidate, &output->lock->outputs, link) { - if (candidate == output || !candidate->surface) { - continue; - } - - if (candidate->surface->surface->mapped) { - next_focus = candidate->surface->surface; - break; - } - } - - focus_surface(output->lock, next_focus); - } -} - -static void handle_surface_map(struct wl_listener *listener, void *data) { - struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); - if (surf->lock->focused == NULL) { - focus_surface(surf->lock, surf->surface->surface); - } - cursor_rebase_all(); -} - -static void handle_surface_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_output *output = - wl_container_of(listener, output, surface_destroy); - refocus_output(output); - - sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); - output->surface = NULL; - wl_list_remove(&output->surface_destroy.link); - wl_list_remove(&output->surface_map.link); -} - -static void lock_output_reconfigure(struct sway_session_lock_output *output) { - int width = output->output->width; - int height = output->output->height; - - wlr_scene_rect_set_size(output->background, width, height); - - if (output->surface) { - wlr_session_lock_surface_v1_configure(output->surface, width, height); - } -} - -void arrange_locks(void) { - if (server.session_lock.lock == NULL) { - return; - } - - struct sway_session_lock_output *lock_output; - wl_list_for_each(lock_output, &server.session_lock.lock->outputs, link) { - lock_output_reconfigure(lock_output); - } -} - -static void handle_new_surface(struct wl_listener *listener, void *data) { - struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); - struct wlr_session_lock_surface_v1 *lock_surface = data; - struct sway_output *output = lock_surface->output->data; - - sway_log(SWAY_DEBUG, "new lock layer surface"); - - struct sway_session_lock_output *current_lock_output, *lock_output = NULL; - wl_list_for_each(current_lock_output, &lock->outputs, link) { - if (current_lock_output->output == output) { - lock_output = current_lock_output; - break; - } - } - sway_assert(lock_output, "Couldn't find output to lock"); - sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); - - lock_output->surface = lock_surface; - - wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); - - lock_output->surface_destroy.notify = handle_surface_destroy; - wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); - lock_output->surface_map.notify = handle_surface_map; - wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map); - - lock_output_reconfigure(lock_output); -} - -static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { - if (output->surface) { - refocus_output(output); - wl_list_remove(&output->surface_destroy.link); - wl_list_remove(&output->surface_map.link); - } - - wl_list_remove(&output->destroy.link); - wl_list_remove(&output->link); - - free(output); -} - -static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_output *output = - wl_container_of(listener, output, destroy); - sway_session_lock_output_destroy(output); -} - -static struct sway_session_lock_output *session_lock_output_create( - struct sway_session_lock *lock, struct sway_output *output) { - struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); - if (!lock_output) { - sway_log(SWAY_ERROR, "failed to allocate a session lock output"); - return NULL; - } - - struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); - if (!tree) { - sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); - free(lock_output); - return NULL; - } - - struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){ - lock->abandoned ? 1.f : 0.f, - 0.f, - 0.f, - 1.f, - }); - if (!background) { - sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); - wlr_scene_node_destroy(&tree->node); - free(lock_output); - return NULL; - } - - lock_output->output = output; - lock_output->tree = tree; - lock_output->background = background; - lock_output->lock = lock; - - lock_output->destroy.notify = lock_node_handle_destroy; - wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); - - lock_output_reconfigure(lock_output); - - wl_list_insert(&lock->outputs, &lock_output->link); - - return lock_output; -} - -static void sway_session_lock_destroy(struct sway_session_lock* lock) { - struct sway_session_lock_output *lock_output, *tmp_lock_output; - wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { - // destroying the node will also destroy the whole lock output - wlr_scene_node_destroy(&lock_output->tree->node); - } - - if (server.session_lock.lock == lock) { - server.session_lock.lock = NULL; - } - - if (!lock->abandoned) { - wl_list_remove(&lock->destroy.link); - wl_list_remove(&lock->unlock.link); - wl_list_remove(&lock->new_surface.link); - } - - free(lock); -} - -static void handle_unlock(struct wl_listener *listener, void *data) { - struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); - sway_log(SWAY_DEBUG, "session unlocked"); - - sway_session_lock_destroy(lock); - - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - // copied from seat_set_focus_layer -- deduplicate? - struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); - if (previous) { - // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, NULL); - seat_set_focus(seat, previous); - } - } - - // Triggers a refocus of the topmost surface layer if necessary - // TODO: Make layer surface focus per-output based on cursor position - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - arrange_layers(output); - } - - // Views are now visible, so check if we need to activate inhibition again. - sway_idle_inhibit_v1_check_active(); -} - -static void handle_abandon(struct wl_listener *listener, void *data) { - struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); - sway_log(SWAY_INFO, "session lock abandoned"); - - struct sway_session_lock_output *lock_output; - wl_list_for_each(lock_output, &lock->outputs, link) { - wlr_scene_rect_set_color(lock_output->background, - (float[4]){ 1.f, 0.f, 0.f, 1.f }); - } - - lock->abandoned = true; - wl_list_remove(&lock->destroy.link); - wl_list_remove(&lock->unlock.link); - wl_list_remove(&lock->new_surface.link); -} - -static void handle_session_lock(struct wl_listener *listener, void *data) { - struct wlr_session_lock_v1 *lock = data; - struct wl_client *client = wl_resource_get_client(lock->resource); - - if (server.session_lock.lock) { - if (server.session_lock.lock->abandoned) { - sway_log(SWAY_INFO, "Replacing abandoned lock"); - sway_session_lock_destroy(server.session_lock.lock); - } else { - sway_log(SWAY_ERROR, "Cannot lock an already locked session"); - wlr_session_lock_v1_destroy(lock); - return; - } - } - - struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock)); - if (!sway_lock) { - sway_log(SWAY_ERROR, "failed to allocate a session lock object"); - wlr_session_lock_v1_destroy(lock); - return; - } - - wl_list_init(&sway_lock->outputs); - - sway_log(SWAY_DEBUG, "session locked"); - - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seat_unfocus_unless_client(seat, client); - } - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - sway_session_lock_add_output(sway_lock, output); - } - - sway_lock->new_surface.notify = handle_new_surface; - wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); - sway_lock->unlock.notify = handle_unlock; - wl_signal_add(&lock->events.unlock, &sway_lock->unlock); - sway_lock->destroy.notify = handle_abandon; - wl_signal_add(&lock->events.destroy, &sway_lock->destroy); - - wlr_session_lock_v1_send_locked(lock); - server.session_lock.lock = sway_lock; - - // The lock screen covers everything, so check if any active inhibition got - // deactivated due to lost visibility. - sway_idle_inhibit_v1_check_active(); -} - -static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { - // if the server shuts down while a lock is active, destroy the lock - if (server.session_lock.lock) { - sway_session_lock_destroy(server.session_lock.lock); - } - - wl_list_remove(&server.session_lock.new_lock.link); - wl_list_remove(&server.session_lock.manager_destroy.link); - - server.session_lock.manager = NULL; -} - -void sway_session_lock_add_output(struct sway_session_lock *lock, - struct sway_output *output) { - struct sway_session_lock_output *lock_output = - session_lock_output_create(lock, output); - - // if we run out of memory while trying to lock the screen, the best we - // can do is kill the sway process. Security conscious users will have - // the sway session fall back to a login shell. - if (!lock_output) { - sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); - abort(); - } -} - -bool sway_session_lock_has_surface(struct sway_session_lock *lock, - struct wlr_surface *surface) { - struct sway_session_lock_output *lock_output; - wl_list_for_each(lock_output, &lock->outputs, link) { - if (lock_output->surface && lock_output->surface->surface == surface) { - return true; - } - } - - return false; -} - -bool sway_session_lock_init(void) { - server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); - if (!server.session_lock.manager) { - sway_log(SWAY_ERROR, "Failed to create session lock manager"); - return false; - } - - server.session_lock.new_lock.notify = handle_session_lock; - server.session_lock.manager_destroy.notify = handle_session_lock_destroy; - wl_signal_add(&server.session_lock.manager->events.new_lock, - &server.session_lock.new_lock); - wl_signal_add(&server.session_lock.manager->events.destroy, - &server.session_lock.manager_destroy); - return true; -} diff --git a/sway/main.c b/sway/main.c index a94389266..b118a1823 100644 --- a/sway/main.c +++ b/sway/main.c @@ -1,24 +1,23 @@ +#define _POSIX_C_SOURCE 200809L #include #include -#include #include #include #include #include +#include #include -#include #include #include #include #include #include #include -#include #include "sway/commands.h" #include "sway/config.h" +#include "sway/debug.h" #include "sway/server.h" #include "sway/swaynag.h" -#include "sway/desktop/transaction.h" #include "sway/tree/root.h" #include "sway/ipc-server.h" #include "ipc-client.h" @@ -28,9 +27,7 @@ static bool terminate_request = false; static int exit_value = 0; -static struct rlimit original_nofile_rlimit = {0}; struct sway_server server = {0}; -struct sway_debug debug = {0}; void sway_terminate(int exit_code) { if (!server.wl_display) { @@ -45,12 +42,90 @@ void sway_terminate(int exit_code) { } } +void sig_handler(int signal) { + sway_terminate(EXIT_SUCCESS); +} + +void detect_raspi(void) { + bool raspi = false; + FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); + if (!f) { + return; + } + char *line = NULL; + size_t line_size = 0; + while (getline(&line, &line_size, f) != -1) { + if (strstr(line, "Raspberry Pi")) { + raspi = true; + break; + } + } + fclose(f); + FILE *g = fopen("/proc/modules", "r"); + if (!g) { + free(line); + return; + } + bool vc4 = false; + while (getline(&line, &line_size, g) != -1) { + if (strstr(line, "vc4")) { + vc4 = true; + break; + } + } + free(line); + fclose(g); + if (!vc4 && raspi) { + fprintf(stderr, "\x1B[1;31mWarning: You have a " + "Raspberry Pi, but the vc4 Module is " + "not loaded! Set 'dtoverlay=vc4-kms-v3d'" + "in /boot/config.txt and reboot.\x1B[0m\n"); + } +} + +void detect_proprietary(int allow_unsupported_gpu) { + FILE *f = fopen("/proc/modules", "r"); + if (!f) { + return; + } + char *line = NULL; + size_t line_size = 0; + while (getline(&line, &line_size, f) != -1) { + if (strstr(line, "nvidia")) { + if (allow_unsupported_gpu) { + sway_log(SWAY_ERROR, + "!!! Proprietary Nvidia drivers are in use !!!"); + } else { + sway_log(SWAY_ERROR, + "Proprietary Nvidia drivers are NOT supported. " + "Use Nouveau. To launch sway anyway, launch with " + "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); + exit(EXIT_FAILURE); + } + break; + } + if (strstr(line, "fglrx")) { + if (allow_unsupported_gpu) { + sway_log(SWAY_ERROR, + "!!! Proprietary AMD drivers are in use !!!"); + } else { + sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support " + "Wayland. Use radeon. To try anyway, launch sway with " + "--unsupported-gpu and DO NOT report issues."); + exit(EXIT_FAILURE); + } + break; + } + } + free(line); + fclose(f); +} + void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); char *resp = ipc_single_command(socketfd, IPC_COMMAND, command, &len); printf("%s\n", resp); - free(resp); close(socketfd); } @@ -62,8 +137,7 @@ static void log_env(void) { "SWAYSOCK", }; for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { - char *value = getenv(log_vars[i]); - sway_log(SWAY_INFO, "%s=%s", log_vars[i], value != NULL ? value : ""); + sway_log(SWAY_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); } } @@ -108,126 +182,74 @@ static void log_kernel(void) { pclose(f); } -static void restore_nofile_limit(void) { - if (original_nofile_rlimit.rlim_cur == 0) { - return; + +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) { + sway_log(SWAY_ERROR, "Unable to drop root, refusing to start"); + return false; + } } - if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { - sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " - "setrlimit(NOFILE) failed"); + if (setuid(0) != -1) { + sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " + "restore it after setuid), refusing to start"); + return false; } -} - -static void increase_nofile_limit(void) { - if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { - sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " - "getrlimit(NOFILE) failed"); - return; - } - - struct rlimit new_rlimit = original_nofile_rlimit; - new_rlimit.rlim_cur = new_rlimit.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) { - sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " - "setrlimit(NOFILE) failed"); - sway_log(SWAY_INFO, "Running with %d max open files", - (int)original_nofile_rlimit.rlim_cur); - return; - } - - pthread_atfork(NULL, NULL, restore_nofile_limit); -} - -static int term_signal(int signal, void *data) { - sway_terminate(EXIT_SUCCESS); - return 0; -} - -static void restore_signals(void) { - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - - struct sigaction sa_dfl = { .sa_handler = SIG_DFL }; - sigaction(SIGCHLD, &sa_dfl, NULL); - sigaction(SIGPIPE, &sa_dfl, NULL); -} - -static void init_signals(void) { - wl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL); - wl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL); - - struct sigaction sa_ign = { .sa_handler = SIG_IGN }; - // avoid need to reap children - sigaction(SIGCHLD, &sa_ign, NULL); - // prevent ipc write errors from crashing sway - sigaction(SIGPIPE, &sa_ign, NULL); - - pthread_atfork(NULL, NULL, restore_signals); + return true; } void enable_debug_flag(const char *flag) { - if (strcmp(flag, "noatomic") == 0) { + if (strcmp(flag, "damage=highlight") == 0) { + debug.damage = DAMAGE_HIGHLIGHT; + } else if (strcmp(flag, "damage=rerender") == 0) { + 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) { debug.txn_timings = true; - } else if (has_prefix(flag, "txn-timeout=")) { - server.txn_timeout_ms = atoi(&flag[strlen("txn-timeout=")]); - } else { - sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); + } else if (strncmp(flag, "txn-timeout=", 12) == 0) { + server.txn_timeout_ms = atoi(&flag[12]); } } -static sway_log_importance_t convert_wlr_log_importance( - enum wlr_log_importance importance) { - switch (importance) { - case WLR_ERROR: - return SWAY_ERROR; - case WLR_INFO: - return SWAY_INFO; - default: - return SWAY_DEBUG; - } -} - -static void handle_wlr_log(enum wlr_log_importance importance, - const char *fmt, va_list args) { - static char sway_fmt[1024]; - snprintf(sway_fmt, sizeof(sway_fmt), "[wlr] %s", fmt); - _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); -} - -static const struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"config", required_argument, NULL, 'c'}, - {"validate", no_argument, NULL, 'C'}, - {"debug", no_argument, NULL, 'd'}, - {"version", no_argument, NULL, 'v'}, - {"verbose", no_argument, NULL, 'V'}, - {"get-socketpath", no_argument, NULL, 'p'}, - {"unsupported-gpu", no_argument, NULL, 'u'}, - {0, 0, 0, 0} -}; - -static const char usage[] = - "Usage: sway [options] [command]\n" - "\n" - " -h, --help Show help message and quit.\n" - " -c, --config Specify a config file.\n" - " -C, --validate Check the validity of the config file, then exit.\n" - " -d, --debug Enables full logging, including debug information.\n" - " -v, --version Show the version number and quit.\n" - " -V, --verbose Enables more verbose logging.\n" - " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" - "\n"; - int main(int argc, char **argv) { - bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; + static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; + + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"config", required_argument, NULL, 'c'}, + {"validate", no_argument, NULL, 'C'}, + {"debug", no_argument, NULL, 'd'}, + {"version", no_argument, NULL, 'v'}, + {"verbose", no_argument, NULL, 'V'}, + {"get-socketpath", no_argument, NULL, 'p'}, + {"unsupported-gpu", no_argument, NULL, 'u'}, + {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'}, + {0, 0, 0, 0} + }; char *config_path = NULL; + const char* usage = + "Usage: sway [options] [command]\n" + "\n" + " -h, --help Show help message and quit.\n" + " -c, --config Specify a config file.\n" + " -C, --validate Check the validity of the config file, then exit.\n" + " -d, --debug Enables full logging, including debug information.\n" + " -v, --version Show the version number and quit.\n" + " -V, --verbose Enables more verbose logging.\n" + " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" + "\n"; + int c; while (1) { int option_index = 0; @@ -237,35 +259,34 @@ int main(int argc, char **argv) { } switch (c) { case 'h': // help - printf("%s", usage); + fprintf(stdout, "%s", usage); exit(EXIT_SUCCESS); break; case 'c': // config - free(config_path); config_path = strdup(optarg); break; case 'C': // validate - validate = true; + validate = 1; break; case 'd': // debug - debug = true; + debug = 1; break; case 'D': // extended debug options enable_debug_flag(optarg); break; case 'u': - allow_unsupported_gpu = true; + allow_unsupported_gpu = 1; break; case 'v': // version - printf("sway version " SWAY_VERSION "\n"); + fprintf(stdout, "sway version " SWAY_VERSION "\n"); exit(EXIT_SUCCESS); break; case 'V': // verbose - verbose = true; + verbose = 1; break; - case 'p': // --get-socketpath + case 'p': ; // --get-socketpath if (getenv("SWAYSOCK")) { - printf("%s\n", getenv("SWAYSOCK")); + fprintf(stdout, "%s\n", getenv("SWAYSOCK")); exit(EXIT_SUCCESS); } else { fprintf(stderr, "sway socket not detected.\n"); @@ -286,30 +307,24 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } - char *unsupported_gpu_env = getenv("SWAY_UNSUPPORTED_GPU"); - // we let the flag override the environment variable - if (!allow_unsupported_gpu && unsupported_gpu_env) { - allow_unsupported_gpu = parse_boolean(unsupported_gpu_env, false); - } - // As the 'callback' function for wlr_log is equivalent to that for // sway, we do not need to override it. if (debug) { sway_log_init(SWAY_DEBUG, sway_terminate); - wlr_log_init(WLR_DEBUG, handle_wlr_log); - } else if (verbose) { + wlr_log_init(WLR_DEBUG, NULL); + } else if (verbose || validate) { sway_log_init(SWAY_INFO, sway_terminate); - wlr_log_init(WLR_INFO, handle_wlr_log); + wlr_log_init(WLR_INFO, NULL); } else { sway_log_init(SWAY_ERROR, sway_terminate); - wlr_log_init(WLR_ERROR, handle_wlr_log); + wlr_log_init(WLR_ERROR, NULL); } - sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); - sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR); log_kernel(); log_distro(); log_env(); + detect_proprietary(allow_unsupported_gpu); + detect_raspi(); if (optind < argc) { // Behave as IPC client if (optind != 1) { @@ -322,6 +337,9 @@ int main(int argc, char **argv) { "`sway -d 2>sway.log`."); exit(EXIT_FAILURE); } + if (!drop_permissions()) { + exit(EXIT_FAILURE); + } char *socket_path = getenv("SWAYSOCK"); if (!socket_path) { sway_log(SWAY_ERROR, "Unable to retrieve socket path"); @@ -329,22 +347,30 @@ int main(int argc, char **argv) { } char *command = join_args(argv + optind, argc - optind); run_as_ipc_client(command, socket_path); - free(command); return 0; } - increase_nofile_limit(); - - sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); - - if (!server_init(&server)) { + if (!server_privileged_prepare(&server)) { return 1; } - init_signals(); + if (!drop_permissions()) { + server_fini(&server); + exit(EXIT_FAILURE); + } - if (server.linux_dmabuf_v1) { - wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); + // handle SIGTERM signals + signal(SIGTERM, sig_handler); + + // prevent ipc from crashing sway + signal(SIGPIPE, SIG_IGN); + + sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); + + root = root_create(); + + if (!server_init(&server)) { + return 1; } if (validate) { @@ -361,38 +387,19 @@ int main(int argc, char **argv) { goto shutdown; } - set_rr_scheduling(); - if (!server_start(&server)) { sway_terminate(EXIT_FAILURE); goto shutdown; } config->active = true; - force_modeset(); load_swaybars(); run_deferred_commands(); - run_deferred_bindings(); - transaction_commit_dirty(); - if (config->swaynag_config_errors.client != NULL) { + if (config->swaynag_config_errors.pid > 0) { swaynag_show(&config->swaynag_config_errors); } - struct swaynag_instance nag_gpu = (struct swaynag_instance){ - .args = "--type error " - "--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' " - "--detailed-message", - .detailed = true, - }; - - if (unsupported_gpu_detected && !allow_unsupported_gpu) { - swaynag_log(config->swaynag_command, &nag_gpu, - "To remove this message, launch sway with --unsupported-gpu " - "or set the environment variable SWAY_UNSUPPORTED_GPU=true."); - swaynag_show(&nag_gpu); - } - server_run(&server); shutdown: @@ -405,10 +412,6 @@ shutdown: free(config_path); free_config(config); - if (nag_gpu.client != NULL) { - wl_client_destroy(nag_gpu.client); - } - pango_cairo_font_map_set_default(NULL); return exit_value; diff --git a/sway/meson.build b/sway/meson.build index cb03a4d28..293a4ed20 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -2,47 +2,40 @@ sway_sources = files( 'commands.c', 'config.c', 'criteria.c', + 'debug-tree.c', 'decoration.c', 'ipc-json.c', 'ipc-server.c', - 'lock.c', 'main.c', - 'realtime.c', - 'scene_descriptor.c', + 'security.c', 'server.c', - 'sway_text_node.c', 'swaynag.c', - 'xdg_activation_v1.c', 'xdg_decoration.c', + 'desktop/desktop.c', 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', - 'desktop/tearing.c', + 'desktop/render.c', 'desktop/transaction.c', + 'desktop/xdg_shell_v6.c', 'desktop/xdg_shell.c', - 'desktop/launcher.c', 'input/input-manager.c', - 'input/cursor.c', - 'input/keyboard.c', 'input/seat.c', - 'input/seatop_default.c', 'input/seatop_down.c', 'input/seatop_move_floating.c', 'input/seatop_move_tiling.c', 'input/seatop_resize_floating.c', 'input/seatop_resize_tiling.c', - 'input/switch.c', - 'input/tablet.c', - 'input/text_input.c', + 'input/cursor.c', + 'input/keyboard.c', 'config/bar.c', 'config/output.c', 'config/seat.c', 'config/input.c', - 'commands/allow_tearing.c', 'commands/assign.c', 'commands/bar.c', 'commands/bind.c', @@ -68,12 +61,9 @@ sway_sources = files( 'commands/force_focus_wrapping.c', 'commands/fullscreen.c', 'commands/gaps.c', - 'commands/gesture.c', 'commands/hide_edge_borders.c', - 'commands/inhibit_idle.c', 'commands/kill.c', 'commands/mark.c', - 'commands/max_render_time.c', 'commands/opacity.c', 'commands/include.c', 'commands/input.c', @@ -81,13 +71,10 @@ 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', 'commands/popup_during_fullscreen.c', - 'commands/primary_selection.c', 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', @@ -97,14 +84,9 @@ sway_sources = files( 'commands/seat/cursor.c', 'commands/seat/fallback.c', 'commands/seat/hide_cursor.c', - 'commands/seat/idle.c', - 'commands/seat/keyboard_grouping.c', 'commands/seat/pointer_constraint.c', - 'commands/seat/shortcuts_inhibitor.c', - 'commands/seat/xcursor_theme.c', 'commands/set.c', 'commands/show_marks.c', - 'commands/shortcuts_inhibitor.c', 'commands/smart_borders.c', 'commands/smart_gaps.c', 'commands/split.c', @@ -150,64 +132,43 @@ sway_sources = files( 'commands/bar/tray_output.c', 'commands/bar/tray_padding.c', 'commands/bar/workspace_buttons.c', - 'commands/bar/workspace_min_width.c', 'commands/bar/wrap_scroll.c', 'commands/input/accel_profile.c', - 'commands/input/calibration_matrix.c', 'commands/input/click_method.c', - 'commands/input/clickfinger_button_map.c', 'commands/input/drag.c', 'commands/input/drag_lock.c', 'commands/input/dwt.c', - 'commands/input/dwtp.c', 'commands/input/events.c', 'commands/input/left_handed.c', 'commands/input/map_from_region.c', 'commands/input/map_to_output.c', - 'commands/input/map_to_region.c', 'commands/input/middle_emulation.c', 'commands/input/natural_scroll.c', 'commands/input/pointer_accel.c', - 'commands/input/rotation_angle.c', 'commands/input/repeat_delay.c', 'commands/input/repeat_rate.c', 'commands/input/scroll_button.c', - 'commands/input/scroll_button_lock.c', 'commands/input/scroll_factor.c', 'commands/input/scroll_method.c', 'commands/input/tap.c', 'commands/input/tap_button_map.c', - 'commands/input/tool_mode.c', 'commands/input/xkb_capslock.c', - 'commands/input/xkb_file.c', 'commands/input/xkb_layout.c', 'commands/input/xkb_model.c', 'commands/input/xkb_numlock.c', 'commands/input/xkb_options.c', 'commands/input/xkb_rules.c', - 'commands/input/xkb_switch_layout.c', 'commands/input/xkb_variant.c', - 'commands/output/adaptive_sync.c', - 'commands/output/allow_tearing.c', 'commands/output/background.c', 'commands/output/disable.c', 'commands/output/dpms.c', 'commands/output/enable.c', - 'commands/output/hdr.c', - 'commands/output/max_render_time.c', 'commands/output/mode.c', 'commands/output/position.c', - 'commands/output/power.c', - 'commands/output/render_bit_depth.c', 'commands/output/scale.c', - 'commands/output/scale_filter.c', - 'commands/output/subpixel.c', - 'commands/output/toggle.c', 'commands/output/transform.c', - 'commands/output/unplug.c', - 'commands/output/color_profile.c', 'tree/arrange.c', 'tree/container.c', @@ -220,34 +181,28 @@ sway_sources = files( sway_deps = [ cairo, - drm, + gdk_pixbuf, jsonc, libevdev, libinput, - libudev, math, pango, - pcre2, + pcre, pixman, - threads, + server_protos, wayland_server, wlroots, xkbcommon, - xcb, - xcb_icccm, ] -if wlroots_features['xwayland'] +if have_xwayland sway_sources += 'desktop/xwayland.c' -endif - -if wlroots_features['libinput_backend'] - sway_sources += 'input/libinput.c' + sway_deps += xcb endif executable( 'sway', - sway_sources + wl_protos_src, + sway_sources, include_directories: [sway_inc], dependencies: sway_deps, link_with: [lib_sway_common], diff --git a/sway/realtime.c b/sway/realtime.c deleted file mode 100644 index 11154af0e..000000000 --- a/sway/realtime.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -#include -#include "sway/server.h" -#include "log.h" - -static void child_fork_callback(void) { - struct sched_param param; - - param.sched_priority = 0; - - int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); - if (ret != 0) { - sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork"); - } -} - -void set_rr_scheduling(void) { - int prio = sched_get_priority_min(SCHED_RR); - int old_policy; - int ret; - struct sched_param param; - - ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); - if (ret != 0) { - sway_log(SWAY_DEBUG, "Failed to get old scheduling priority"); - return; - } - - param.sched_priority = prio; - - ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); - if (ret != 0) { - sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio); - return; - } - - pthread_atfork(NULL, NULL, child_fork_callback); -} diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c deleted file mode 100644 index 92bdda00c..000000000 --- a/sway/scene_descriptor.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include "log.h" -#include "sway/scene_descriptor.h" - -struct scene_descriptor { - void *data; - struct wlr_addon addon; -}; - -static const struct wlr_addon_interface addon_interface; - -static struct scene_descriptor *scene_node_get_descriptor( - struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { - struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface); - if (!addon) { - return NULL; - } - - struct scene_descriptor *desc = wl_container_of(addon, desc, addon); - return desc; -} - -static void descriptor_destroy(struct scene_descriptor *desc) { - wlr_addon_finish(&desc->addon); - free(desc); -} - -void *scene_descriptor_try_get(struct wlr_scene_node *node, - enum sway_scene_descriptor_type type) { - struct scene_descriptor *desc = scene_node_get_descriptor(node, type); - if (!desc) { - return NULL; - } - - return desc->data; -} - -void scene_descriptor_destroy(struct wlr_scene_node *node, - enum sway_scene_descriptor_type type) { - struct scene_descriptor *desc = scene_node_get_descriptor(node, type); - if (!desc) { - return; - } - descriptor_destroy(desc); -} - -static void addon_handle_destroy(struct wlr_addon *addon) { - struct scene_descriptor *desc = wl_container_of(addon, desc, addon); - descriptor_destroy(desc); -} - -static const struct wlr_addon_interface addon_interface = { - .name = "sway_scene_descriptor", - .destroy = addon_handle_destroy, -}; - -bool scene_descriptor_assign(struct wlr_scene_node *node, - enum sway_scene_descriptor_type type, void *data) { - struct scene_descriptor *desc = calloc(1, sizeof(*desc)); - if (!desc) { - sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); - return false; - } - - wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface); - desc->data = data; - return true; -} diff --git a/sway/security.c b/sway/security.c new file mode 100644 index 000000000..6a00229ed --- /dev/null +++ b/sway/security.c @@ -0,0 +1,18 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include "sway/security.h" + +struct command_policy *alloc_command_policy(const char *command) { + struct command_policy *policy = malloc(sizeof(struct command_policy)); + if (!policy) { + return NULL; + } + policy->command = strdup(command); + if (!policy->command) { + free(policy); + return NULL; + } + policy->context = 0; + return policy; +} diff --git a/sway/server.c b/sway/server.c index 8bdafb674..712d8022b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -1,58 +1,27 @@ +#define _POSIX_C_SOURCE 200112L #include #include #include -#include -#include +#include #include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include #include -#include -#include #include -#include -#include #include -#include -#include -#include -#include -#include -#include +#include #include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include #include +#include #include -#include #include -#include -#include -#include -#include -#include #include -#include #include -#include -#include -#include #include -#include -#include #include "config.h" #include "list.h" #include "log.h" @@ -61,334 +30,67 @@ #include "sway/input/input-manager.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/input/cursor.h" #include "sway/tree/root.h" -#include "sway/tree/workspace.h" - -#if WLR_HAS_XWAYLAND -#include +#if HAVE_XWAYLAND #include "sway/xwayland.h" #endif -#if WLR_HAS_DRM_BACKEND -#include -#endif - -#define SWAY_XDG_SHELL_VERSION 5 -#define SWAY_LAYER_SHELL_VERSION 5 -#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 -#define SWAY_PRESENTATION_VERSION 2 - -bool unsupported_gpu_detected = false; - -#if WLR_HAS_DRM_BACKEND -static void handle_drm_lease_request(struct wl_listener *listener, void *data) { - /* We only offer non-desktop outputs, but in the future we might want to do - * more logic here. */ - - struct wlr_drm_lease_request_v1 *req = data; - struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); - if (!lease) { - sway_log(SWAY_ERROR, "Failed to grant lease request"); - wlr_drm_lease_request_v1_reject(req); - } -} -#endif - -static bool is_privileged(const struct wl_global *global) { -#if WLR_HAS_DRM_BACKEND - if (server.drm_lease_manager != NULL) { - struct wlr_drm_lease_device_v1 *drm_lease_dev; - wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { - if (drm_lease_dev->global == global) { - return true; - } - } - } -#endif - - return - global == server.output_manager_v1->global || - global == server.output_power_manager_v1->global || - global == server.input_method->global || - global == server.foreign_toplevel_list->global || - global == server.foreign_toplevel_manager->global || - global == server.wlr_data_control_manager_v1->global || - global == server.ext_data_control_manager_v1->global || - global == server.screencopy_manager_v1->global || - global == server.ext_image_copy_capture_manager_v1->global || - global == server.export_dmabuf_manager_v1->global || - global == server.security_context_manager_v1->global || - global == server.gamma_control_manager_v1->global || - global == server.layer_shell->global || - global == server.session_lock.manager->global || - global == server.input->keyboard_shortcuts_inhibit->global || - global == server.input->virtual_keyboard->global || - global == server.input->virtual_pointer->global || - global == server.input->transient_seat_manager->global || - global == server.xdg_output_manager_v1->global; -} - -static bool filter_global(const struct wl_client *client, - const struct wl_global *global, void *data) { -#if WLR_HAS_XWAYLAND - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - if (xwayland && global == xwayland->shell_v1->global) { - return xwayland->server != NULL && client == xwayland->server->client; - } -#endif - - // Restrict usage of privileged protocols to unsandboxed clients - // TODO: add a way for users to configure an allow-list - const struct wlr_security_context_v1_state *security_context = - wlr_security_context_manager_v1_lookup_client( - server.security_context_manager_v1, (struct wl_client *)client); - if (is_privileged(global)) { - return security_context == NULL; - } - - return true; -} - -static void detect_proprietary(struct wlr_backend *backend, void *data) { - int drm_fd = wlr_backend_get_drm_fd(backend); - if (drm_fd < 0) { - return; - } - - drmVersion *version = drmGetVersion(drm_fd); - if (version == NULL) { - sway_log(SWAY_ERROR, "drmGetVersion() failed"); - return; - } - - if (strcmp(version->name, "nvidia-drm") == 0) { - unsupported_gpu_detected = true; - sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); - } - - if (strcmp(version->name, "evdi") == 0) { - unsupported_gpu_detected = true; - sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); - } - - drmFreeVersion(version); -} - -static void do_renderer_recreate(void *data) { - struct sway_server *server = data; - server->recreating_renderer = NULL; - - sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); - struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); - if (renderer == NULL) { - sway_log(SWAY_ERROR, "Unable to create renderer"); - return; - } - - struct wlr_allocator *allocator = - wlr_allocator_autocreate(server->backend, renderer); - if (allocator == NULL) { - sway_log(SWAY_ERROR, "Unable to create allocator"); - wlr_renderer_destroy(renderer); - return; - } - - struct wlr_renderer *old_renderer = server->renderer; - struct wlr_allocator *old_allocator = server->allocator; - server->renderer = renderer; - server->allocator = allocator; - - wl_list_remove(&server->renderer_lost.link); - wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); - - wlr_compositor_set_renderer(server->compositor, renderer); - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - wlr_output_init_render(output->wlr_output, - server->allocator, server->renderer); - } - - wlr_allocator_destroy(old_allocator); - wlr_renderer_destroy(old_renderer); -} - -static void handle_renderer_lost(struct wl_listener *listener, void *data) { - struct sway_server *server = wl_container_of(listener, server, renderer_lost); - - if (server->recreating_renderer != NULL) { - sway_log(SWAY_DEBUG, "Re-creation of renderer already scheduled"); - return; - } - - sway_log(SWAY_INFO, "Scheduling re-creation of renderer after GPU reset"); - server->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server); -} - -static void handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, void *data) { - struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data; - struct sway_view *view = request->toplevel_handle->data; - - if (view->image_capture_source == NULL) { - view->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node( - &view->image_capture_scene->tree.node, server.wl_event_loop, server.allocator, server.renderer); - if (view->image_capture_source == NULL) { - return; - } - } - - wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(request, view->image_capture_source); -} - -bool server_init(struct sway_server *server) { - sway_log(SWAY_DEBUG, "Initializing Wayland server"); +bool server_privileged_prepare(struct sway_server *server) { + sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); server->wl_display = wl_display_create(); - if (!server->wl_display) { - sway_log(SWAY_ERROR, "Failed to create wl_display"); - return false; - } server->wl_event_loop = wl_display_get_event_loop(server->wl_display); + server->backend = wlr_backend_autocreate(server->wl_display, NULL); + server->noop_backend = wlr_noop_backend_create(server->wl_display); - wl_display_set_global_filter(server->wl_display, filter_global, NULL); - wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); - - if (!wlr_fixes_create(server->wl_display, 1)) { - sway_log(SWAY_ERROR, "Failed to create wp_fixes global"); - return false; - } - root = root_create(server->wl_display); - if (!root) { - sway_log(SWAY_ERROR, "Failed to create root"); - wl_display_destroy(server->wl_display); - return false; - } - - server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session); if (!server->backend) { sway_log(SWAY_ERROR, "Unable to create backend"); return false; } + return true; +} - wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); +bool server_init(struct sway_server *server) { + sway_log(SWAY_DEBUG, "Initializing Wayland server"); - server->renderer = wlr_renderer_autocreate(server->backend); - if (!server->renderer) { - sway_log(SWAY_ERROR, "Failed to create renderer"); - return false; - } + struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); + assert(renderer); - server->renderer_lost.notify = handle_renderer_lost; - wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); - - wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - - if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && - wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { - server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( - server->wl_display, 5, server->renderer); - if (!server->linux_dmabuf_v1) { - sway_log(SWAY_ERROR, "Failed to create linux-dmabuf v1"); - return false; - } - } - if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && - server->renderer->features.timeline && - server->backend->features.timeline) { - if (!wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, - wlr_renderer_get_drm_fd(server->renderer))) { - sway_log(SWAY_ERROR, "Failed to create linux-drm-syncobj v1"); - return false; - } - } - - server->allocator = wlr_allocator_autocreate(server->backend, - server->renderer); - if (!server->allocator) { - sway_log(SWAY_ERROR, "Failed to create allocator"); - return false; - } - - server->compositor = wlr_compositor_create(server->wl_display, 6, - server->renderer); - if (!server->compositor) { - sway_log(SWAY_ERROR, "Failed to create compositor"); - return false; - } - - if (!wlr_subcompositor_create(server->wl_display)) { - sway_log(SWAY_ERROR, "Failed to create subcompositor"); - return false; - } + wlr_renderer_init_wl_display(renderer, server->wl_display); + server->compositor = wlr_compositor_create(server->wl_display, renderer); server->data_device_manager = wlr_data_device_manager_create(server->wl_display); - if (!server->data_device_manager) { - sway_log(SWAY_ERROR, "Failed to create data device manager"); - return false; - } - server->gamma_control_manager_v1 = - wlr_gamma_control_manager_v1_create(server->wl_display); - if (!server->gamma_control_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create gamma control manager"); - return false; - } - wlr_scene_set_gamma_control_manager_v1(root->root_scene, - server->gamma_control_manager_v1); + 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); server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); - server->xdg_output_manager_v1 = - wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); - if (!server->xdg_output_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create XDG output manager"); - return false; - } + wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); - server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); - if (!server->idle_notifier_v1) { - sway_log(SWAY_ERROR, "Failed to create idle notifier"); - return false; - } - if (!sway_idle_inhibit_manager_v1_init()) { - sway_log(SWAY_ERROR, "Failed to init idle inhibit manager"); - return false; - } + server->idle = wlr_idle_create(server->wl_display); + server->idle_inhibit_manager_v1 = + sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); - server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, - SWAY_LAYER_SHELL_VERSION); - if (!server->layer_shell) { - sway_log(SWAY_ERROR, "Failed to create layer shell"); - return false; - } + server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); wl_signal_add(&server->layer_shell->events.new_surface, &server->layer_shell_surface); server->layer_shell_surface.notify = handle_layer_shell_surface; - server->xdg_shell = wlr_xdg_shell_create(server->wl_display, - SWAY_XDG_SHELL_VERSION); - if (!server->xdg_shell) { - sway_log(SWAY_ERROR, "Failed to create XDG shell"); - return false; - } - wl_signal_add(&server->xdg_shell->events.new_toplevel, - &server->xdg_shell_toplevel); - server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; + server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display); + wl_signal_add(&server->xdg_shell_v6->events.new_surface, + &server->xdg_shell_v6_surface); + server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface; - server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); - if (!server->tablet_v2) { - sway_log(SWAY_ERROR, "Failed to create tablet manager"); - return false; - } + server->xdg_shell = wlr_xdg_shell_create(server->wl_display); + wl_signal_add(&server->xdg_shell->events.new_surface, + &server->xdg_shell_surface); + server->xdg_shell_surface.notify = handle_xdg_shell_surface; server->server_decoration_manager = wlr_server_decoration_manager_create(server->wl_display); - if (!server->server_decoration_manager) { - sway_log(SWAY_ERROR, "Failed to create server decoration manager"); - return false; - } wlr_server_decoration_manager_set_default_mode( server->server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); @@ -399,10 +101,6 @@ bool server_init(struct sway_server *server) { server->xdg_decoration_manager = wlr_xdg_decoration_manager_v1_create(server->wl_display); - if (!server->xdg_decoration_manager) { - sway_log(SWAY_ERROR, "Failed to create XDG decoration manager"); - return false; - } wl_signal_add( &server->xdg_decoration_manager->events.new_toplevel_decoration, &server->xdg_decoration); @@ -411,288 +109,29 @@ bool server_init(struct sway_server *server) { server->relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server->wl_display); - if (!server->relative_pointer_manager) { - sway_log(SWAY_ERROR, "Failed to create relative pointer manager"); - return false; - } server->pointer_constraints = wlr_pointer_constraints_v1_create(server->wl_display); - if (!server->pointer_constraints) { - sway_log(SWAY_ERROR, "Failed to create pointer constraints"); - return false; - } server->pointer_constraint.notify = handle_pointer_constraint; wl_signal_add(&server->pointer_constraints->events.new_constraint, &server->pointer_constraint); - if (!wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION)) { - sway_log(SWAY_ERROR, "Failed to create presentation"); - return false; - } - if (!wlr_alpha_modifier_v1_create(server->wl_display)) { - sway_log(SWAY_ERROR, "Failed to create alpha modifier"); - return false; - } + server->presentation = + wlr_presentation_create(server->wl_display, server->backend); - server->output_manager_v1 = - wlr_output_manager_v1_create(server->wl_display); - if (!server->output_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create output manager"); - return false; - } - 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); - - server->output_power_manager_v1 = - wlr_output_power_manager_v1_create(server->wl_display); - if (!server->output_power_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create output power manager"); - return false; - } - server->output_power_manager_set_mode.notify = - handle_output_power_manager_set_mode; - wl_signal_add(&server->output_power_manager_v1->events.set_mode, - &server->output_power_manager_set_mode); - server->input_method = wlr_input_method_manager_v2_create(server->wl_display); - if (!server->input_method) { - sway_log(SWAY_ERROR, "Failed to create input method manager"); - return false; - } - server->text_input = wlr_text_input_manager_v3_create(server->wl_display); - if (!server->text_input) { - sway_log(SWAY_ERROR, "Failed to create text input manager"); - return false; - } - server->foreign_toplevel_list = - wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); - if (!server->foreign_toplevel_list) { - sway_log(SWAY_ERROR, "Failed to create foreign toplevel list"); - return false; - } - server->foreign_toplevel_manager = - wlr_foreign_toplevel_manager_v1_create(server->wl_display); - if (!server->foreign_toplevel_manager) { - sway_log(SWAY_ERROR, "Failed to create foreign toplevel manager"); - return false; - } - - if (!sway_session_lock_init()) { - return false; - } - if (!sway_ext_workspace_init()) { - return false; - } - -#if WLR_HAS_DRM_BACKEND - server->drm_lease_manager= - wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); - if (server->drm_lease_manager) { - server->drm_lease_request.notify = handle_drm_lease_request; - wl_signal_add(&server->drm_lease_manager->events.request, - &server->drm_lease_request); - } else { - sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); - sway_log(SWAY_INFO, "VR will not be available"); - } -#endif - - server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); - if (!server->export_dmabuf_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create export dmabuf manager"); - return false; - } - server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); - if (!server->screencopy_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create screencopy manager"); - return false; - } - server->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); - if (!server->ext_image_copy_capture_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create ext image copy capture manager"); - return false; - } - if (!wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1)) { - sway_log(SWAY_ERROR, "Failed to create ext output image capture source manager"); - return false; - } - server->wlr_data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); - if (!server->wlr_data_control_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create data control manager"); - return false; - } - server->ext_data_control_manager_v1 = wlr_ext_data_control_manager_v1_create(server->wl_display, 1); - if (!server->ext_data_control_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create ext data control manager"); - return false; - } - server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); - if (!server->security_context_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create security context manager"); - return false; - } - if (!wlr_viewporter_create(server->wl_display)) { - sway_log(SWAY_ERROR, "Failed to create viewporter"); - return false; - } - if (!wlr_single_pixel_buffer_manager_v1_create(server->wl_display)) { - sway_log(SWAY_ERROR, "Failed to create single pixel buffer manager"); - return false; - } - server->content_type_manager_v1 = - wlr_content_type_manager_v1_create(server->wl_display, 1); - if (!server->content_type_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create content type manager"); - return false; - } - if (!wlr_fractional_scale_manager_v1_create(server->wl_display, 1)) { - sway_log(SWAY_ERROR, "Failed to create fractional scale manager"); - return false; - } - - server->ext_foreign_toplevel_image_capture_source_manager_v1 = - wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(server->wl_display, 1); - if (!server->ext_foreign_toplevel_image_capture_source_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create ext foreign toplevel image capture source manager"); - return false; - } - server->new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request; - wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request, - &server->new_foreign_toplevel_capture_request); - - server->tearing_control_v1 = - wlr_tearing_control_manager_v1_create(server->wl_display, 1); - if (!server->tearing_control_v1) { - sway_log(SWAY_ERROR, "Failed to create tearing control manager"); - return false; - } - server->tearing_control_new_object.notify = handle_new_tearing_hint; - wl_signal_add(&server->tearing_control_v1->events.new_object, - &server->tearing_control_new_object); - wl_list_init(&server->tearing_controllers); - - struct wlr_xdg_foreign_registry *foreign_registry = - wlr_xdg_foreign_registry_create(server->wl_display); - if (!foreign_registry) { - sway_log(SWAY_ERROR, "Failed to create XDG foreign registry"); - return false; - } - if (!wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry)) { - sway_log(SWAY_ERROR, "Failed to create XDG foreign v1"); - return false; - } - if (!wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry)) { - sway_log(SWAY_ERROR, "Failed to create XDG foreign v2"); - return false; - } - - server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display); - if (!server->xdg_activation_v1) { - sway_log(SWAY_ERROR, "Failed to create XDG activation"); - return false; - } - server->xdg_activation_v1_request_activate.notify = - xdg_activation_v1_handle_request_activate; - wl_signal_add(&server->xdg_activation_v1->events.request_activate, - &server->xdg_activation_v1_request_activate); - server->xdg_activation_v1_new_token.notify = - xdg_activation_v1_handle_new_token; - wl_signal_add(&server->xdg_activation_v1->events.new_token, - &server->xdg_activation_v1_new_token); - - struct wlr_xdg_toplevel_tag_manager_v1 *xdg_toplevel_tag_manager_v1 = - wlr_xdg_toplevel_tag_manager_v1_create(server->wl_display, 1); - if (!xdg_toplevel_tag_manager_v1) { - sway_log(SWAY_ERROR, "Failed to create XDG toplevel tag manager"); - return false; - } - server->xdg_toplevel_tag_manager_v1_set_tag.notify = - xdg_toplevel_tag_manager_v1_handle_set_tag; - wl_signal_add(&xdg_toplevel_tag_manager_v1->events.set_tag, - &server->xdg_toplevel_tag_manager_v1_set_tag); - - struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = - wlr_cursor_shape_manager_v1_create(server->wl_display, 2); - if (!cursor_shape_manager) { - sway_log(SWAY_ERROR, "Failed to create cursor shape manager"); - return false; - } - server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; - wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); - - if (server->renderer->features.input_color_transform) { - const enum wp_color_manager_v1_render_intent render_intents[] = { - WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL, - }; - size_t transfer_functions_len = 0; - enum wp_color_manager_v1_transfer_function *transfer_functions = - wlr_color_manager_v1_transfer_function_list_from_renderer(server->renderer, &transfer_functions_len); - size_t primaries_len = 0; - enum wp_color_manager_v1_primaries *primaries = - wlr_color_manager_v1_primaries_list_from_renderer(server->renderer, &primaries_len); - struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( - server->wl_display, 2, &(struct wlr_color_manager_v1_options){ - .features = { - .parametric = true, - .set_mastering_display_primaries = true, - }, - .render_intents = render_intents, - .render_intents_len = sizeof(render_intents) / sizeof(render_intents[0]), - .transfer_functions = transfer_functions, - .transfer_functions_len = transfer_functions_len, - .primaries = primaries, - .primaries_len = primaries_len, - }); - free(transfer_functions); - free(primaries); - if (!cm) { - sway_log(SWAY_ERROR, "Failed to create color manager"); - return false; - } - wlr_scene_set_color_manager_v1(root->root_scene, cm); - } - - if (!wlr_color_representation_manager_v1_create_with_renderer( - server->wl_display, 1, server->renderer)) { - sway_log(SWAY_ERROR, "Failed to create color representation manager"); - return false; - } - - wl_list_init(&server->pending_launcher_ctxs); - - // Avoid using "wayland-0" as display socket - char name_candidate[16]; - for (unsigned int i = 1; i <= 32; ++i) { - snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); - if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { - server->socket = strdup(name_candidate); - break; - } - } + 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); + server->socket = wl_display_add_socket_auto(server->wl_display); if (!server->socket) { sway_log(SWAY_ERROR, "Unable to open wayland socket"); wlr_backend_destroy(server->backend); return false; } - server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); - if (!server->headless_backend) { - sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); - wlr_backend_destroy(server->backend); - return false; - } else { - wlr_multi_backend_add(server->backend, server->headless_backend); - } - - struct wlr_output *wlr_output = - wlr_headless_add_output(server->headless_backend, 800, 600); - wlr_output_set_name(wlr_output, "FALLBACK"); - root->fallback_output = output_create(wlr_output); + struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend); + root->noop_output = output_create(wlr_output); // This may have been set already via -Dtxn-timeout if (!server->txn_timeout_ms) { @@ -700,92 +139,63 @@ bool server_init(struct sway_server *server) { } server->dirty_nodes = create_list(); - if (!server->dirty_nodes) { - sway_log(SWAY_ERROR, "Failed to create dirty nodes list"); - return false; - } + server->transactions = create_list(); server->input = input_manager_create(server); - if (!server->input) { - sway_log(SWAY_ERROR, "Failed to create input manager"); - return false; - } input_manager_get_default_seat(); // create seat0 return true; } void server_fini(struct sway_server *server) { - // remove listeners - wl_list_remove(&server->renderer_lost.link); - wl_list_remove(&server->new_output.link); - wl_list_remove(&server->layer_shell_surface.link); - wl_list_remove(&server->xdg_shell_toplevel.link); - wl_list_remove(&server->server_decoration.link); - wl_list_remove(&server->xdg_decoration.link); - wl_list_remove(&server->pointer_constraint.link); - wl_list_remove(&server->output_manager_apply.link); - wl_list_remove(&server->output_manager_test.link); - wl_list_remove(&server->output_power_manager_set_mode.link); -#if WLR_HAS_DRM_BACKEND - if (server->drm_lease_manager) { - wl_list_remove(&server->drm_lease_request.link); - } -#endif - wl_list_remove(&server->tearing_control_new_object.link); - wl_list_remove(&server->xdg_activation_v1_request_activate.link); - wl_list_remove(&server->xdg_activation_v1_new_token.link); - wl_list_remove(&server->xdg_toplevel_tag_manager_v1_set_tag.link); - wl_list_remove(&server->request_set_cursor_shape.link); - wl_list_remove(&server->new_foreign_toplevel_capture_request.link); - wl_list_remove(&server->workspace_manager_v1_commit.link); - input_manager_finish(server->input); - // TODO: free sway-specific resources -#if WLR_HAS_XWAYLAND - if (server->xwayland.wlr_xwayland != NULL) { - wl_list_remove(&server->xwayland_surface.link); - wl_list_remove(&server->xwayland_ready.link); - wlr_xwayland_destroy(server->xwayland.wlr_xwayland); - } +#if HAVE_XWAYLAND + wlr_xwayland_destroy(server->xwayland.wlr_xwayland); #endif wl_display_destroy_clients(server->wl_display); - wlr_backend_destroy(server->backend); wl_display_destroy(server->wl_display); list_free(server->dirty_nodes); - free(server->socket); + list_free(server->transactions); } bool server_start(struct sway_server *server) { -#if WLR_HAS_XWAYLAND - if (config->xwayland != XWAYLAND_MODE_DISABLED) { - sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", - config->xwayland == XWAYLAND_MODE_LAZY); + // TODO: configurable cursor theme and size + int cursor_size = 24; + const char *cursor_theme = NULL; + + char cursor_size_fmt[16]; + snprintf(cursor_size_fmt, sizeof(cursor_size_fmt), "%d", cursor_size); + setenv("XCURSOR_SIZE", cursor_size_fmt, 1); + if (cursor_theme != NULL) { + setenv("XCURSOR_THEME", cursor_theme, 1); + } + +#if HAVE_XWAYLAND + if (config->xwayland) { + sway_log(SWAY_DEBUG, "Initializing Xwayland"); server->xwayland.wlr_xwayland = - wlr_xwayland_create(server->wl_display, server->compositor, - config->xwayland == XWAYLAND_MODE_LAZY); - if (!server->xwayland.wlr_xwayland) { - sway_log(SWAY_ERROR, "Failed to start Xwayland"); - unsetenv("DISPLAY"); - } else { - wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, - &server->xwayland_surface); - server->xwayland_surface.notify = handle_xwayland_surface; - wl_signal_add(&server->xwayland.wlr_xwayland->events.ready, - &server->xwayland_ready); - server->xwayland_ready.notify = handle_xwayland_ready; + wlr_xwayland_create(server->wl_display, server->compositor, true); + wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, + &server->xwayland_surface); + server->xwayland_surface.notify = handle_xwayland_surface; + wl_signal_add(&server->xwayland.wlr_xwayland->events.ready, + &server->xwayland_ready); + server->xwayland_ready.notify = handle_xwayland_ready; - setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true); - - /* xcursor configured by the default seat */ + server->xwayland.xcursor_manager = + wlr_xcursor_manager_create(cursor_theme, cursor_size); + wlr_xcursor_manager_load(server->xwayland.xcursor_manager, 1); + struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( + server->xwayland.xcursor_manager, "left_ptr", 1); + if (xcursor != NULL) { + struct wlr_xcursor_image *image = xcursor->images[0]; + wlr_xwayland_set_cursor(server->xwayland.wlr_xwayland, image->buffer, + image->width * 4, image->width, image->height, image->hotspot_x, + image->hotspot_y); } } #endif - if (config->primary_selection) { - wlr_primary_selection_v1_device_manager_create(server->wl_display); - } - sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", server->socket); if (!wlr_backend_start(server->backend)) { @@ -793,7 +203,6 @@ bool server_start(struct sway_server *server) { wlr_backend_destroy(server->backend); return false; } - return true; } diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index a1733aa2b..13827e5ee 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -10,38 +10,57 @@ Sway allows configuring swaybar in the sway configuration file. # COMMANDS -The following commands may only be used in the 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 + + 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 -_ + +*pango_markup* enabled|disabled + Enables or disables pango markup for status lines. This has no effect on + status lines using the i3bar JSON protocol. *id* Sets the ID of the bar. +*position* top|bottom + Sets position of the bar. Default is _bottom_. + +*output* + Restrict the bar to a certain output, can be specified multiple times. If + the output command is omitted, the bar will be displayed on all outputs. + *swaybar_command* Executes custom bar command. Default is _swaybar_. -The following commands may be used either in the configuration file or at -runtime. +*font* + Specifies the font to be used in the bar. -*bindcode* [--release] - Executes _command_ when the mouse button has been pressed (or if _released_ - is given, when the button has been released). The buttons can be given as - an event code, which can be obtaining from *libinput debug-events*. To - disable the default behavior for a button, use the command _nop_. +*separator_symbol* + Specifies the separator symbol to separate blocks on the bar. -*bindsym* [--release] button[1-9]| - Executes _command_ when the mouse button has been pressed (or if _released_ - is given, when the button has been released). The buttons can be given as a - x11 button number or an event name, which can be obtained from *libinput - debug-events*. To disable the default behavior for a button, use the - command _nop_. +*wrap_scroll* yes|no + Enables or disables wrapping when scrolling through workspaces with the + scroll wheel. Default is _no_. + +*workspace_buttons* yes|no + Enables or disables workspace buttons on the bar. Default is _yes_. + +*strip_workspace_name* yes|no + If set to _yes_, then workspace names will be omitted from the workspace + button and only the custom number will be shown. Default is _no_. + +*strip_workspace_numbers* yes|no + If set to _yes_, then workspace numbers will be omitted from the workspace + button and only the custom name will be shown. Default is _no_. *binding_mode_indicator* yes|no Enable or disable binding mode indicator. Default is _yes_. -*font* - Specifies the font to be used in the bar. _font_ should be specified as a - pango font description. For more information on pango font descriptions, - see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description - *gaps* | | Sets the gaps from the edge of the screen for the bar. Gaps can either be set all at once, per direction, or per side. Note that only sides that @@ -52,102 +71,44 @@ runtime. *height* Sets the height of the bar. Default height (0) will match the font size. -*hidden_state* hide|show [] +*bindcode* [--release] + Executes _command_ when the mouse button has been pressed (or if _released_ + is given, when the button has been released). The buttons can be given as + an event code, which can be obtaining from `libinput debug-events`. To + disable the default behavior for a button, use the command _nop_. + +*bindsym* [--release] button[1-9]| + Executes _command_ when the mouse button has been pressed (or if _released_ + is given, when the button has been released). The buttons can be given as a + x11 button number or an event name, which can be obtained from `libinput + debug-events`. To disable the default behavior for a button, use the + command _nop_. + +*mode* dock|hide|invisible + 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_. + +*hidden_state* hide|show Specifies the behaviour of the bar when it is in _hide_ mode. When the hidden state is _hide_, then it is normally hidden, and only unhidden by pressing the modifier key or in case of urgency hints. When the hidden state is _show_, then it is permanently visible, drawn on top of the currently visible workspace. Default is _hide_. - For compatibility with i3, _bar hidden_state hide|show []_ is - supported along with the sway only _bar hidden_state hide|show_ - syntax. When using the i3 syntax, if _bar-id_ is omitted, the hidden_state - will be changed for all bars. Attempting to use _bar - hidden_state hide|show _ will result in an error due to - conflicting bar ids. - -*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. 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_. - - For compatibility with i3, _bar mode []_ syntax is supported - along with the sway only _bar mode _ syntax. When using the - i3 syntax, if _bar-id_ is omitted, the mode will be changed for all bars. - Attempting to use _bar mode _ will result in an - error due to conflicting bar ids. - *modifier* |none Specifies the modifier key that shows a hidden bar. Default is _Mod4_. -*output* |\* - Restrict the bar to a certain output, can be specified multiple times. If - the output command is omitted, the bar will be displayed on all outputs. _\*_ - can be given at any point to reset it back to all outputs. - -*pango_markup* enabled|disabled - Enables or disables pango markup for status lines. This has no effect on - status lines using the i3bar JSON protocol. - -*position* top|bottom - Sets position of the bar. Default is _bottom_. - -*separator_symbol* - Specifies the separator symbol to separate blocks on the bar. - -*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 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 -_ - -*status_edge_padding* - Sets the padding that is used when the status line is at the right edge of - the bar. This value will be multiplied by the output scale. The default is - _3_. - *status_padding* Sets the vertical padding that is used for the status line. The default is _1_. If _padding_ is _0_, blocks will be able to take up the full height of the bar. This value will be multiplied by the output scale. -*strip_workspace_name* yes|no - If set to _yes_, then workspace names will be omitted from the workspace - button and only the custom number will be shown. Default is _no_. - -*strip_workspace_numbers* yes|no - If set to _yes_, then workspace numbers will be omitted from the workspace - button and only the custom name will be shown. Default is _no_. - -*unbindcode* [--release] - Removes the binding with the given . - -*unbindsym* [--release] button[1-9]| - Removes the binding with the given