[
  {
    "path": ".builds/alpine.yml",
    "content": "image: alpine/edge\npackages:\n  - cairo-dev\n  - eudev-dev\n  - gdk-pixbuf-dev\n  - json-c-dev\n  - lcms2-dev\n  - libdisplay-info-dev\n  - libevdev-dev\n  - libinput-dev\n  - libseat-dev\n  - libxcb-dev\n  - libxkbcommon-dev\n  - mesa-dev\n  - meson\n  - pango-dev\n  - pcre2-dev\n  - pixman-dev\n  - scdoc\n  - wayland-dev\n  - wayland-protocols\n  - xcb-util-image-dev\n  - xcb-util-wm-dev\n  - xwayland-dev\n  - hwdata-dev\nsources:\n  - https://github.com/swaywm/sway\n  - https://gitlab.freedesktop.org/wlroots/wlroots.git\ntasks:\n  - wlroots: |\n      cd wlroots\n      meson setup --prefix=/usr build -Dexamples=false\n      ninja -C build\n      sudo ninja -C build install\n  - setup: |\n      cd sway\n      meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled\n  - build: |\n      cd sway\n      ninja -C build\n  - build-no-xwayland: |\n      cd wlroots\n      meson configure build -Dxwayland=disabled\n      ninja -C build\n      sudo ninja -C build install\n\n      cd ../sway\n      meson configure build --clearcache\n      ninja -C build\n  - build-static: |\n      cd sway\n      mkdir subprojects\n      ln -s ../../wlroots subprojects/wlroots\n      rm -rf build\n      meson setup build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots\n      ninja -C build\n"
  },
  {
    "path": ".builds/archlinux.yml",
    "content": "image: archlinux\npackages:\n  - cairo\n  - gdk-pixbuf2\n  - json-c\n  - lcms2\n  - libdisplay-info\n  - libegl\n  - libinput\n  - libxcb\n  - libxkbcommon\n  - meson\n  - pango\n  - pcre2\n  - scdoc\n  - wayland\n  - wayland-protocols\n  - xcb-util-image\n  - xcb-util-wm\n  - xorg-xwayland\n  - seatd\n  - hwdata\nsources:\n  - https://github.com/swaywm/sway\n  - https://gitlab.freedesktop.org/wlroots/wlroots.git\ntasks:\n  - wlroots: |\n      cd wlroots\n      meson setup --prefix=/usr build -Dexamples=false\n      ninja -C build\n      sudo ninja -C build install\n  - setup: |\n      cd sway\n      meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd\n  - build: |\n      cd sway\n      ninja -C build\n"
  },
  {
    "path": ".builds/freebsd.yml",
    "content": "image: freebsd/latest\npackages:\n- devel/basu\n- devel/json-c\n- devel/libevdev\n- devel/meson\n- devel/pcre2\n- devel/pkgconf\n- graphics/cairo\n- graphics/gdk-pixbuf2\n- graphics/lcms2\n- graphics/wayland\n- graphics/wayland-protocols\n- textproc/scdoc\n- x11-toolkits/pango\n- x11/libxcb\n- x11/libxkbcommon\n# wlroots dependencies\n- devel/evdev-proto\n- devel/libepoll-shim\n- devel/libudev-devd\n- graphics/libdrm\n- graphics/mesa-libs\n- sysutils/libdisplay-info\n- sysutils/seatd\n- x11/libinput\n- x11/libX11\n- x11/pixman\n- x11/xcb-util-wm\n- x11-servers/xwayland\n- misc/hwdata\nsources:\n- https://github.com/swaywm/sway\n- https://gitlab.freedesktop.org/wlroots/wlroots.git\ntasks:\n- setup: |\n    cd sway\n    mkdir subprojects\n    cd subprojects\n    ln -s ../../wlroots wlroots\n    cd ..\n    meson setup build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu\n- build: |\n    cd sway\n    ninja -C build\n"
  },
  {
    "path": ".editorconfig",
    "content": "# For the full list of code style requirements, see CONTRIBUTING.md\n\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\n\n[*.{c,h,cmake,txt}]\nindent_style = tab\nindent_size = 4\n\n[*.{xml,yml}]\nindent_style = space\nindent_size = 2\n\n[config]\nindent_style = space\nindent_size = 4\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bugs\nabout: Crashes and other bugs\nlabels: 'bug'\n\n---\n\n### Please read the following before submitting:\n- Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on Libera Chat.\n- Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway.\n- 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.\n  If you fix a script or find outdated information, don't hesitate to adjust the wiki page.\n\n### Please fill out the following:\n- **Sway Version:**\n  - `swaymsg -t get_version` or `sway -v`\n\n- **Debug Log:**\n  - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com.\n  - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway.\n  - Attach the **full** file, do not truncate it.\n\n- **Configuration File:**\n  - Please try to produce with the default configuration.\n  - If you cannot reproduce with the default configuration, please try to find the minimal configuration to reproduce.\n  - Upload the config to a pastebin such as gist.github.com.\n\n- **Stack Trace:**\n  - This is only needed if sway crashes.\n  - If you use systemd, you should be able to open the coredump of the most recent crash with gdb with\n    `coredumpctl gdb sway` and then `bt full` to obtain the stack trace.\n  - 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.\n\n- **Description:**\n  - The steps you took in plain English to reproduce the problem.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Questions\n    url: \"https://libera.chat\"\n    about: \"Please ask questions on IRC in #sway on Libera Chat\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/enhancement.md",
    "content": "---\nname: Enhancements\nabout: New functionality\nlabels: 'enhancement'\n\n---\n\n### Please read the following before submitting:\n- We are not accepting any new window management features unless they get implemented by i3.\n\n### Please fill out the following:\n- **Description:**\nPlease describe in plain English what the enhancement is and what the use case is.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/i3_compat.md",
    "content": "---\nname: i3 Compatibility\nabout: Sway behaves differently from or lacks i3 functionality\nlabels: 'i3-compat'\n\n---\n\n### Please read the following before submitting:\n- The following either do not make sense for Wayland or we have decided against supporting:\n  - `restart`\n  - `resource`\n  - saving and loading layouts\n  - the i3 sync protocol\n\n### Please fill out the following:\n- **i3 PR:**\n  - If this is new i3 functionality, please add a link to the i3 pull request.\n\n- **Description:**\n  - Please describe in plain English how Sway and i3's behaviors differ or what functionality Sway is lacking.\n"
  },
  {
    "path": ".gitignore",
    "content": "install_manifest.txt\n*.swp\n*.o\n*.a\nbin/\ntest/\nbuild/\nbuild-*/\n.cache/\n.lvimrc\nconfig-debug\nwayland-*-protocol.*\n/subprojects/wlroots\nsubprojects\n"
  },
  {
    "path": ".mailmap",
    "content": "Ronan Pigott <ronan@rjp.ie> <rpigott@berkeley.edu>\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to sway\n\nContributing just involves sending a pull request. You will probably be more\nsuccessful with your contribution if you visit #sway-devel on Libera Chat\nupfront and discuss your plans.\n\nNote: rules are made to be broken. Adjust or ignore any/all of these as you see\nfit, but be prepared to justify it to your peers.\n\n## Scope of future changes to sway\n\n**Important**: Sway has completed its core value proposition: it is a fully\nfeatured Wayland-compatible replacement for i3. It is not our intention to\nexpand on the scope of what i3 aims to accomplish. Our priorities now are\nincreasing the stability, reliability, and performance of sway within its\ncurrent scope. For this reason, most new window management feature requests are\nnot accepted, even if accompanied by a patch.\n\n## Pull Requests\n\nIf you already have your own pull request habits, feel free to use them. If you\ndon't, however, allow me to make a suggestion: feature branches pulled from\nupstream. Try this:\n\n1. Fork sway\n2. `git clone https://github.com/username/sway && cd sway`\n3. `git remote add upstream https://github.com/swaywm/sway`\n\nYou only need to do this once. You're never going to use your fork's master\nbranch. Instead, when you start working on a feature, do this:\n\n1. `git fetch upstream`\n2. `git checkout -b add-so-and-so-feature upstream/master`\n3. Add and commit your changes\n4. `git push -u origin add-so-and-so-feature`\n5. Make a pull request from your feature branch\n\nWhen you submit your pull request, your commit log should do most of the talking\nwhen it comes to describing your changes and their motivation. In addition to\nthis, your pull request's comments will ideally include a test plan that the\nreviewers can use to (1) demonstrate the problem on master, if applicable and\n(2) verify that the problem no longer exists with your changes applied (or that\nyour new features work correctly). Document all of the edge cases you're aware\nof so we can adequately test them - then verify the test plan yourself before\nsubmitting.\n\n## Commit Messages\n\nPlease strive to write good commit messages. Here's some guidelines to follow:\n\nThe first line should be limited to 50 characters and should be a sentence that\ncompletes the thought [When applied, this commit will...] *\"Implement\ncmd_move\"* or *\"Fix #742\"* or *\"Improve performance of arrange_windows on ARM\"*\nor similar.\n\nThe subsequent lines should be separated from the subject line by a single\nblank line, and include optional details. In this you can give justification\nfor the change, [reference Github\nissues](https://help.github.com/articles/closing-issues-via-commit-messages/),\nor explain some of the subtler details of your patch. This is important because\nwhen someone finds a line of code they don't understand later, they can use the\n`git blame` command to find out what the author was thinking when they wrote\nit. It's also easier to review your pull requests if they're separated into\nlogical commits that have good commit messages and justify themselves in the\nextended commit description.\n\nAs a good rule of thumb, anything you might put into the pull request\ndescription on Github is probably fair game for going into the extended commit\nmessage as well.\n\nSee [here](https://chris.beams.io/posts/git-commit/) for more details.\n\n## Code Review\n\nWhen your changes are submitted for review, one or more core committers will\nlook over them. Smaller changes might be merged with little fanfare, but larger\nchanges will typically see review from several people. Be prepared to receive\nsome feedback - you may be asked to make changes to your work. Our code review\nprocess is:\n\n1. **Triage** the pull request. Do the commit messages make sense? Is a test\n   plan necessary and/or present? Add anyone as reviewers that you think should\n   be there (using the relevant GitHub feature, if you have the permissions, or\n   with an @mention if necessary).\n2. **Review** the code. Look for code style violations, naming convention\n   violations, buffer overflows, memory leaks, logic errors, non-portable code\n   (including GNU-isms), etc. For significant changes to the public API, loop in\n   a couple more people for discussion.\n3. **Execute** the test plan, if present.\n4. **Merge** the pull request when all reviewers approve.\n5. **File** follow-up tickets if appropriate.\n\n## Style Reference\n\nSway is written in C with a style similar to the [kernel\nstyle](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but\nwith a few notable differences.\n\nTry to keep your code conforming to C11 and POSIX as much as possible, and do\nnot use GNU extensions.\n\n### Brackets\n\nBrackets always go on the same line, including in functions.\nAlways include brackets for if/while/for, even if it's a single statement.\n```c\nvoid function(void) {\n\tif (condition1) {\n\t\tdo_thing1();\n\t}\n\n\tif (condition2) {\n\t\tdo_thing2();\n\t} else {\n\t\tdo_thing3();\n\t}\n}\n```\n\n### Indentation\n\nIndentations are a single tab.\n\nFor long lines that need to be broken, the continuation line should be indented\nwith an additional tab.\n\nIf the line being broken is opening a new block (functions, if, while, etc.),\nthe continuation line should be indented with two tabs, so they can't be\nmisread as being part of the block.\n\n```c\nreally_long_function(argument1, argument2, ...,\n\targument3, argument4);\n\nif (condition1 && condition2 && ...\n\t\tcondition3 && condition4) {\n\tdo_thing();\n}\n```\n\nTry to break the line in the place which you think is the most appropriate to\nbalance the lines.\n\n### Line Length\n\nTry to keep your lines under 80 columns, assuming a tab width equal to 4 spaces,\nbut you can go up to 100 if it improves readability. Don't break lines\nindiscriminately, try to find nice breaking points so your code is easy to read.\n\n### Names\n\nGlobal function and type names should be prefixed with `sway_submodule_` (e.g.\n`struct sway_output`, `sway_output_destroy`).  For static functions and\ntypes local to a file, the names chosen aren't as important. Static functions\nshouldn't have a `sway_` prefix.\n\nFor include guards, use the header's filename relative to include.  Uppercase\nall of the characters, and replace any invalid characters with an underscore.\n\n### Construction/Destruction Functions\n\nFor functions that are responsible for constructing and destructing an object,\nthey should be written as a pair of one of two forms:\n\n* `init`/`finish`: These initialize/deinitialize a type, but are **NOT**\n  responsible for allocating it. They should accept a pointer to some\n  pre-allocated memory (e.g. a member of a struct).\n* `create`/`destroy`: These also initialize/deinitialize, but will return a\n  pointer to a `malloc`ed chunk of memory, and will `free` it in `destroy`.\n\nA destruction function should always be able to accept a NULL pointer or a\nzeroed value and exit cleanly; this simplifies error handling a lot.\n\n### Error Codes\n\nFor functions not returning a value, they should return a (stdbool.h) bool to\nindicated if they succeeded or not.\n\n### Macros\n\nKeep the use of macros to a minimum, especially if a function can do the job. If\nyou do need to use them, try to keep them close to where they're being used and\n`#undef` them after.\n\n### Example\n\n```c\nstruct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {\n\tstruct wlr_backend *backend;\n\tif (getenv(\"WAYLAND_DISPLAY\") || getenv(\"_WAYLAND_DISPLAY\")) {\n\t\tbackend = attempt_wl_backend(display);\n\t\tif (backend) {\n\t\t\treturn backend;\n\t\t}\n\t}\n\n\tconst char *x11_display = getenv(\"DISPLAY\");\n\tif (x11_display) {\n\t\treturn wlr_x11_backend_create(display, x11_display);\n\t}\n\n\t// Attempt DRM+libinput\n\n\tstruct wlr_session *session = wlr_session_create(display);\n\tif (!session) {\n\t\twlr_log(WLR_ERROR, \"Failed to start a DRM session\");\n\t\treturn NULL;\n\t}\n\n\tint gpu = wlr_session_find_gpu(session);\n\tif (gpu == -1) {\n\t\twlr_log(WLR_ERROR, \"Failed to open DRM device\");\n\t\tgoto error_session;\n\t}\n\n\tbackend = wlr_multi_backend_create(session);\n\tif (!backend) {\n\t\tgoto error_gpu;\n\t}\n\n\tstruct wlr_backend *libinput = wlr_libinput_backend_create(display, session);\n\tif (!libinput) {\n\t\tgoto error_multi;\n\t}\n\n\tstruct wlr_backend *drm = wlr_drm_backend_create(display, session, gpu);\n\tif (!drm) {\n\t\tgoto error_libinput;\n\t}\n\n\twlr_multi_backend_add(backend, libinput);\n\twlr_multi_backend_add(backend, drm);\n\treturn backend;\n\nerror_libinput:\n\twlr_backend_destroy(libinput);\nerror_multi:\n\twlr_backend_destroy(backend);\nerror_gpu:\n\twlr_session_close_file(session, gpu);\nerror_session:\n\twlr_session_destroy(session);\n\treturn NULL;\n}\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2016-2017 Drew DeVault\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.ar.md",
    "content": "# sway\n\nsway \n\nهو مدير للمجموعات المركبة لـ[Wayland] متوافق مع [i3] -\n\nإقرأ [الأسئلة الشائعة](https://github.com/swaywm/sway/wiki)\n\n انضم الى [قناة IRC](https://web.libera.chat/gamja/?channels=#sway)\n\n\n## تواقيع الإصدار\n تٌوقع الإصدارات بـواسطة [E88F5E48] و تُنشر على [GitHub](https://github.com/swaywm/sway/releases)\n\n## التثبيت\n\n### بإستخدام الحزم\n\nيتوفر Sway للعديد من التوزيعات، حاول تثبيت حزمة \"sway\" لتوزيعتك\n### التجميع من المصدر\nإطلع على [صفحة الويكي هذه](https://github.com/swaywm/sway/wiki/Development-Setup) إذا أردت بناء الـHEAD من sway و wlroots لأغراض الفحص والتطوير \n\nتثبيت اللوازم: \n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (optional: system tray)\n* [scdoc] (optional: man pages) \\*\n* git (optional: version info) \\*\n\n_\\* Compile-time dep_\n\nنفذ هذه الأوامر:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## الإعدادات\n\nإذا كنت بالفعل تستخدم i3، فعليك نسخ إعدادات i3 لديك إلى  `~/.config/sway/config` وسوف تعمل تلقائياً.\n\nو إلا عليك نسخ ملف الإعدادات النموذج إلى `config/sway/config` الموجود عادةً في `/etc/sway/config.` \n\n\n## التشغيل\n\nشغل `sway` بإستخدام أمر TTY. \nقد يعمل بعض مدراء العرض مع أنهم غير مدعومون من sway \n(gdm مثلاً يعمل بشكل جيد إلى حد ما)\n\n[en]: https://github.com/swaywm/sway#readme\n[ar]: README.ar.md\n[cs]: README.cs.md\n[de]: README.de.md\n[dk]: README.dk.md\n[es]: README.es.md\n[fr]: README.fr.md\n[ge]: README.ge.md\n[gr]: README.gr.md\n[hi]: README.hi.md\n[hu]: README.hu.md\n[ir]: README.ir.md\n[it]: README.it.md\n[ja]: README.ja.md\n[ko]: README.ko.md\n[nl]: README.nl.md\n[no]: README.no.md\n[pl]: README.pl.md\n[pt]: README.pt.md\n[ro]: README.ro.md\n[ru]: README.ru.md\n[sv]: README.sv.md\n[tr]: README.tr.md\n[uk]: README.uk.md\n[zh-CN]: README.zh-CN.md\n[zh-TW]: README.zh-TW.md\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.az.md",
    "content": "# sway\n\nsway [i3]-ə uyğun [Wayland] kompozitorudur. [Tez-tez verilən sualları] oxuyun. \n[IRC kanalına] qoşulun (\"irc.libera.chat\"-da #sway).\n\n## Buraxılış İmzaları\n\nBuraxılışlar [E88F5E48] ilə imzalanıb və [GitHub-da][GitHub releases] dərc edilib.\n\n## Quraşdırma\n\n### Paketlərdən\n\nSway bir çox distributivdə mövcuddur. Öz distributiviniz üçün \n\"sway\" paketini quraşdırmağa çalışın.\n\n### Mənbə kodundan kompilyasiya\n\nTest və ya inkişaf üçün sway və wlroots-un HEAD-ini qurmaq istəyirsinizsə, \n[bu viki səhifəsini][Development setup] nəzərdən keçirin.\n\nAsılılıqları quraşdırın:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (ixtiyari: sistem trayı üçün əlavə şəkil formatları)\n* [swaybg] (ixtiyari: divar kağızı)\n* [scdoc] (ixtiyari: man səhifələri) \\*\n* git (ixtiyari: versiya məlumatı) \\*\n\n_\\* Kompilyasiya asılılıqları_\n\nBu əmrləri icra edin:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Konfiqurasiya\n\nƏgər artıq i3-dən istifadə edirsinizsə, i3 konfiqurasiyanızı `~/.config/sway/config` \nünvanına köçürün və o, dərhal işləyəcək. Əks halda, nümunə konfiqurasiya faylını \n`~/.config/sway/config` ünvanına köçürün. O, adətən `/etc/sway/config` ünvanında yerləşir.\nKonfiqurasiya haqqında məlumat üçün `man 5 sway` əmrini icra edin.\n\n## İşə Salma\n\nTTY-dan `sway`-ı işə salın. Bəzi ekran menecerləri işləyə bilər, lakin sway tərəfindən \ndəstəklənmir (gdm-in kifayət qədər yaxşı işlədiyi məlumdur).\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[Tez-tez verilən sualları]: https://github.com/swaywm/sway/wiki\n[IRC kanalına]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[swaybg]: https://github.com/swaywm/swaybg/\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.cs.md",
    "content": "# sway\n\nsway je [waylandový][Wayland] kompozitor kompatibilní s [i3]. Přečtěte si\n[FAQ (anglicky)][FAQ]. Připojte se na [IRC kanál (anglicky)][IRC channel]\n\\(#sway na irc.libera.chat).\n\n## Podpisy vydání\n\nVydané verze jsou podepsány klíčem [E88F5E48] a publikovány\n[na GitHubu][GitHub releases].\n\n## Instalace\n\n### Z balíků\n\nSway je dostupný v mnoha distribucích. Zkuste v té vaší nainstalovat balík \"sway\".\n\n### Kompilace ze zdrojových kódů\n\nPokud chcete sestavit HEAD repozitáře sway a wlroots pro testování nebo vývoj,\npoužijte návod na [této stránce na wiki (anglicky)][Development setup].\n\nNainstalujte závislosti:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (volitelné: dodatečné formáty ikon pro oznamovací oblast)\n* [swaybg] (volitelné: tapeta plochy)\n* [scdoc] (volitelné: man stránky) \\*\n* git (volitelné: informace o verzi) \\*\n\n_\\* Závislost pouze pro kompilaci_\n\nSpusťte tyto příkazy:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Konfigurace\n\nPokud již používáte i3, zkopírujte svou konfiguraci i3 do `~/.config/sway/config`\na ta bude ihned fungovat. Jinak zkopírujte do `~/.config/sway/config` ukázkový\nkonfigurační soubor. Ten se obvykle nachází v `/etc/sway/config`.\nPro více informací o konfiguraci spusťte `man 5 sway`.\n\n## Spuštění\n\nSpusťte `sway` z TTY nebo ze správce displeje.\n\n[en]: https://github.com/swaywm/sway#readme\n[ar]: README.ar.md\n[cs]: README.cs.md\n[de]: README.de.md\n[dk]: README.dk.md\n[es]: README.es.md\n[fr]: README.fr.md\n[ge]: README.ge.md\n[gr]: README.gr.md\n[hi]: README.hi.md\n[hu]: README.hu.md\n[ir]: README.ir.md\n[it]: README.it.md\n[ja]: README.ja.md\n[ko]: README.ko.md\n[nl]: README.nl.md\n[no]: README.no.md\n[pl]: README.pl.md\n[pt]: README.pt.md\n[ro]: README.ro.md\n[ru]: README.ru.md\n[sv]: README.sv.md\n[tr]: README.tr.md\n[uk]: README.uk.md\n[zh-CN]: README.zh-CN.md\n[zh-TW]: README.zh-TW.md\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[swaybg]: https://github.com/swaywm/swaybg/\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.de.md",
    "content": "# Sway\nSway ist ein [i3]-kompatibler [Wayland]-Compositor. Lies die [FAQ]. Tritt dem [IRC Channel] bei (#sway on irc.libera.chat; Englisch).\n\n## Signaturen\nJeder Release wird mit dem PGP-Schlüssel [E88F5E48] signiert und [auf GitHub][GitHub releases] veröffentlicht.\n\n## Installation\n\n### Über die Paketverwaltung\n\nSway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Versuche einfach das Paket \"sway\" zu installieren.\n\n### Quellcode selbst kompilieren\n\nsway benötigt die folgenden Pakete:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols\\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt)\n* [swaybg] (Optional, wird für das Setzen von Desktophintergrundbildern benötigt)\n* [scdoc] (Optional, wird für die Dokumentation (Man Pages) benötigt)\\*\n* git (Optional: Versionsinfo)\\*\n\n_\\*Werden nur für das Kompilieren benötigt_\n\nFühre die folgenden Befehle aus:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\nSchaue 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.\n\n## Konfiguration\n\nFalls 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`.\n\n## Sway starten\nSway kann einfach mit dem Befehl `sway` vom TTY oder mithilfe eines Displaymanagers gestartet werden.\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[swaybg]: https://github.com/swaywm/swaybg\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.dk.md",
    "content": "# Sway\n\nSway er en [i3]-kompatibel [Wayland] compositor. Læs [Ofte stillede spørgsmål].\nDeltag på [IRC kanalen][IRC kanal] \\(#sway på irc.libera.chat).\n\n## Udgivelses Signaturer\n\nUdgivelser er signeret med [E88F5E48] og publiceret [på GitHub][GitHub\nreleases].\n\n## Installation\n\n### Fra pakker\n\nSway er tilgængelig i mange distributioner. Prøv at installere \"sway\" pakken\nfra din.\n\nHvis du er interesseret i at pakke Sway til din distribution, kan du tage forbi\nIRC kanalen eller sende en email til sir@cmpwn.com for rådgivning.\n\n### Kompilering fra kildekode\n\nSe [denne wiki-side][Opsætning til udvikling] hvis du vil bygge HEAD af sway og\nwlroots til test eller udvikling.\n\nInstallationsafhængigheder:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (valgfrit: system tray)\n* [scdoc] (valgfrit: man pages) \\*\n* git \\*\n\n_\\*Kompileringsafhængighed_\n\nKør følgende kommandoer:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Konfiguration\n\nHvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til\n`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurationsfilen til\n`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`.  Kør\n`man 5 sway` for at få oplysninger om konfigurationen.\n\n## Eksekvering\n\nKør `sway` fra en TTY eller fra en display manager.\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[Ofte stillede spørgsmål]: https://github.com/swaywm/sway/wiki\n[IRC kanal]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.es.md",
    "content": "# sway\n\nsway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/).\nLea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway on\nirc.libera.chat).\n\n## Firmas de las versiones\n\nLas distintas versiones están firmadas con [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)\ny publicadas [en GitHub](https://github.com/swaywm/sway/releases).\n\n## Instalación\n\n### Usando paquetes\n\nSway está disponible en muchas distribuciones. Pruebe instalando el paquete \"sway\" desde la suya.\nSi no está disponible, puede consultar [esta documentación](https://github.com/swaywm/sway/wiki/Unsupported-packages) \ny así obtener información acerca de como instalarlo.\n\nSi está interesado en crear un paquete para su distribución, únase al canal de IRC  o\nescriba un email a sir@cmpwn.com\n\n### Compilando el código fuente\n\nInstale las dependencias:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (optional: system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \\*\n* git \\*\n\n_\\*Compile-time dep_\n\nDesde su consola, ejecute las órdenes:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Configuración\n\nSi ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y\nsway funcionará sin tener que configurar nada más. En otro caso, copie el archivo de\nconfiguración básico a `~/.config/sway/config`, normalmente se encuentra en `/etc/sway/config`.\nEjecute `man 5 sway` para obtener información sobre la configuración.\n\n## Ejecución\n\nEjecute `sway` desde su consola. Algunos gestores de pantalla pueden funcionar sin estar \nsoportados por `sway` (sabemos que gdm funciona bastante bien).\n"
  },
  {
    "path": "README.fr.md",
    "content": "# sway\n\nSway est un compositeur [Wayland] compatible avec [i3]. Lisez la\n[FAQ]. Rejoignez le [canal IRC] (#sway sur irc.libera.chat).\n\n## Aide en français\n\n[abdelq] fournit du support en français sur IRC et Github, dans le fuseau\nhoraire UTC-4 (EST).\n\n## Signatures de nouvelles versions\n\nLes nouvelles versions sont signées avec [E88F5E48] et publiées\n[sur GitHub][versions GitHub].\n\n## Installation\n\n### À partir de paquets\n\nSway est disponible sur beaucoup de distributions. Essayez d'installer le\npaquet \"sway\" pour la vôtre.\n\nSi vous êtes intéressé à maintenir Sway pour votre distribution, passez sur le\ncanal IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des\nconseils.\n\n### Compilation depuis les sources\n\nConsultez [cette page wiki][Configuration de développement] si vous souhaitez\ncompiler la révision HEAD de sway et wlroots pour tester ou développer.\n\nInstallez les dépendances :\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (optionnel : system tray)\n* [scdoc] (optionnel : requis pour les pages man) \\*\n* git (optionnel : information de version) \\*\n\n_\\* Requis uniquement pour la compilation_\n\nExécutez ces commandes :\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Configuration\n\nSi vous utilisez déjà i3, copiez votre configuration i3 vers\n`~/.config/sway/config` et sway fonctionnera directement. Sinon, copiez\nl'exemple de fichier de configuration vers `~/.config/sway/config`. Il se\ntrouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour lire la\ndocumentation pour la configuration de sway.\n\n## Exécution\n\nExécutez `sway` à partir d'un TTY ou d'un gestionnaires d'affichage.\n\n[Wayland]: http://wayland.freedesktop.org/\n[i3]: https://i3wm.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[canal IRC]: https://web.libera.chat/gamja/?channels=#sway\n[abdelq]: https://github.com/abdelq\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[versions GitHub]: https://github.com/swaywm/sway/releases\n[Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.ge.md",
    "content": "# sway\n\nsway არის [i3]-თავსებადი [Wayland]-ის კომპოზიტორი. მეტი ინფორმაციისთვის იხილეთ \n[FAQ]. დაუკავშირდით [IRC არხს][IRC channel] \\(#sway irc.libera.chat-ზე).\n\n## გამოშვების ხელმოწერები\n\nგამოშვებები ხელმოწერილია [E88F5E48]-ით და გამოქვეყნებულია [GitHub-ზე][GitHub releases].\n\n## ინსტალაცია\n\n### რეპოზიტორიიდან\n\nSway არის ხელმისაწვდომი ბევრი დისტრიბუტაციისთვის. ცადეთ \"sway\" პაკეტის ინსტალაცია თქვენთვის.\n\n### კოდის კომპილაცია\n\nიხილეთ [ეს ვიკი გვერდი][Development setup] თუ გინდათ რომ ააწყოთ sway და wlroots სატესტოდ ან დეველოპმენტისთვის.\n\nდააინსტალირეთ დამოკიდებულებები:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (ასევე არჩევითია: system tray)\n* [scdoc] (ასევე არჩევითია: man pages) \\*\n* git (ასევე არჩევითია: version info) \\*\n\n_\\* Compile-time dep_\n\nგაუშვით ეს ბრძანებები:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## კონფიგურაცია\n\nთუ უკვე იყენებთ i3-ს, მაშინ დააკოპირე i3 კონფიგურაცია და ჩასვი `~/.config/sway/config` \nდა უპრობლემოდ იმუშავებს პირდაპირ. წინააღმდეგ შემთხვევაში კონფიგურაციის ნიმუში ჩააკოპირეთ აქ: `~/.config/sway/config`. კომპიგურაციის ნიმუში ხშირ შემთხვევაში არის `/etc/sway/config`.\nგაუშვი `man 5 sway` კონპიგურაციაზე ინფორმაციის მისაღებად.\n\n## გაშვება\n\nგაუშვი `sway` TTY-ისთვის. ზოგიერთმა ლოგინ მენეჯერმა შეიძლება იმუშავოს, მაგრამ არ\nარის მხარდაჭერილი sway-სგან (როგორც წესი კარგად მუშაობს gdm).\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.gr.md",
    "content": "# Sway\n\nΤο sway ένα [i3]-συμβατό [Wayland] compositor. Διαβάστε το [FAQ]. Μπείτε στο\n[IRC channel] \\(#sway on irc.libera.chat).\n\n## Υπογραφές δημοσιεύσεων\n\nΟι εκδόσεις είναι υπογεραμμένες με [E88F5E48] και δημοσιευμένες [στο GitHub][GitHub releases].\n\n## Εγκατάσταση\n\n### Από πακέτα\n\nΤο Sway είναι διαθέσιμο σε πολλά distributions. Δοκιμάστε εγκαταστώντας το \"sway\" package για\nτο δικό σας.\n\nΕάν ενδιαφέρεστε για packaging του sway για το distribution σας, να πάτε στο IRC\nchannel ή στείλτε ένα email στο sir@cmpwn.com για συμβουλές.\n\n### Compiling από πηγή\n\nΤσεκάρετε [αυτό το wiki page][Development setup] εάμα θέλετε να κάνετε build το HEAD του\nsway και wlroots γιά τεστάρισμα ή development.\n\nΕγκατάσταση των dependencies:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (προαιρετικό: system tray)\n* [scdoc] (προαιρετικό: man pages) \\*\n* git (προαιρετικό: πληροφορίες εκδώσεων) \\*\n\n_\\*Compile-time dep_\n\nΤρέξτε αυτά τα commands:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Configuration\n\nΕάν ήδη χρησιμοποιήτε το i3, αντιγράψτε το i3 config σας στο `~/.config/sway/config` και\nθα δουλέψει out of the box. Αλλιώς, αντιγράψτε το sample configuration αρχείο στο\n`~/.config/sway/config`. Το οποίο συνήθως βρίσκεται στο `/etc/sway/config`.\nΚάντε run `man 5 sway` για πληροφορίες τού configuration.\n\n## Τρέχοντας\n\nΤρέξτε `sway` από ένα TTY. Μερίκα display managers μπορεί να δουλέψουν αλλά δέν είναι συμβατά με\nτο sway (το gdm γνωρίζεται να δουλέβει σχετικά καλά).\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc"
  },
  {
    "path": "README.hi.md",
    "content": "# sway\n\nsway एक [i3](https://i3wm.org/)-अनुकूल\n[Wayland](https://wayland.freedesktop.org/) Compositor है।\n[FAQ](https://github.com/swaywm/sway/wiki) पढिये। [IRC\nChannel](https://web.libera.chat/gamja/?channels=#sway)\n(irc.libera.chat पर #sway) में भी जुडिये।\n\n## रिलीज हस्ताक्षर\n\nरिलीजें\n[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)\nसे साइन होतें हैं और [Github पर](https://github.com/swaywm/sway/releases) प्रकाशित होते हैं।\n\n## इंस्टौलेशन\n\n### पैकेजों के द्वारा\n\nSway कई distributions में उप्लब्ध है। आप अपने में \"sway\" नामक पैकेज इंस्टौल करके देख\nसकते हैं।\n\n### Source से compile करके\n\nयदि आप परीक्षण और विकास के लिए sway और wlroots के नवीनतम संस्करण बनाना\nचाहते हैं, तो [यह विकी\nपृष्ठ](https://github.com/swaywm/sway/wiki/Development-Setup) देखें।\n\nनिर्भरताएं:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf (वैकल्पिक: system tray के लिये)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (वैकल्पिक: man पृष्ठों के लिये)\n  \\*\n* git (वैकल्पिक: संस्करण जानने के लिये)\n\n_\\* Compilation के समय आवश्यक_\n\nये commands चलाएं:\n\n\tmeson setup build/\n\tninja -C build/\n\tsudo ninja -C build/ install\n\n## Configuration\n\nअगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को\n`~/.config/sway/config` में copy कर लीजिये और वह बिना किसी परिवर्तन के काम\nकरेगा। अन्यथा, नमूने configuration file को `~/.config/sway/config` में copy\nकर लीजिये। यह सामान्यतः `/etc/sway/config` में पाया जाता है। `man 5\nsway` से आप configuration के बारे में जानकारी प्राप्त कर सकते हैं।\n\n## चलाना\n\nआप एक tty से `sway` को चला सकते हैं। कुछ display managers काम करते हैं परन्तु ये\nsway के द्वारा समर्थित नहीं है (gdm के बारे में जाना गया है कि वह सही काम करता\nहै)।\n"
  },
  {
    "path": "README.hu.md",
    "content": "# sway\n\nA 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).\n\n## Csomagaláírások\n\nA kiadott csomagok az [E88F5E48] kulccsal vannak aláírva, és [GitHubon][GitHub releases] publikálva.\n\n## Telepítés\n\n### Csomagból\n\nA Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a \"sway\"\ncsomagot telepíteni az általad használt eszközzel.\n\nHa szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC-\ncsatornára, vagy küldj levelet a sir@cmpwn.com címre tanácsokért.\n\n### Fordítás forráskódból\n\nOlvasd el [ezt a wikioldalt][Development setup], ha szeretnéd tesztelési vagy\nfejlesztési célokból lefordítani az aktuális (HEAD) állapotát a `sway`-nek és a\n`wlroots`-nak.\n\nTelepítsd a függőségeket:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (opcionális: system tray)\n* [scdoc] (opcionális: man pages) \\*\n* git (opcionális: version info) \\*\n\n_\\*Fordításidejű függőség_\n\nFuttasd ezeket a parancsokat:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Konfiguráció\n\nHa előzőleg i3-at használtál, akkor átmásolhatod az i3-beállításaidat a\n`~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold\nle kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési\nútvonalon található.\nFuttasd a `man 5 sway` parancsot további információért a konfigurációval\nkapcsolatban.\n\n## Futtatás\n\nFuttasd a `sway` parancsot egy TTY-felületről. Néhány bejelentkezéskezelő\n(display manager) működhet, de alapvetően nem támogatottak a sway által. (A\ngdm-ről ismeretes, hogy egész jól működik.)\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.ir.md",
    "content": "# sway\n\n&rlm;sway یک کامپوزیتور الهام گرفته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال\nIRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (&lrm;#sway&rlm; در\nirc.libera.chat).\n\nبرای حمایت از تیم توسعه sway به [صفحه\nPatreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مراجعه کنید.\n\n## امضای نسخه‌ها\n\nامضای نسخه‌ها با [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) در [GitHub](https://github.com/swaywm/sway/releases) منتشر می‌شود.\n\n## شیوه نصب\n\n### از بسته‌های رسمی\n\n&rlm;sway در بسته‌های رسمی توزیع‌های مختلف وجود دارد. بسته «sway» را نصب کنید. در صورتی که بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صفحه راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید.\n\nاگر به ایجاد بسته sway برای توزیعتان علاقه‌مند هستید، از کانال IRC استفاده کنید یا به sir@cmpwn.com ایمیل بزنید.\n\n### کامپایل کردن کد\n\nچنانچه می‌خواهید آخرین نسخه کد sway و wlroots را برای آزمایش یا توسعه بسازید به این [صفحه راهنما](https://github.com/swaywm/sway/wiki/Development-Setup) مراجعه کنید.\n\nبسته‌های مورد نیاز:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (انتخابی: برای system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (انتخابی: برای صفحه‌های راهنما) \\*\n* git (انتخابی: برای اطلاع در خصوص نسخه‌ها) \\*\n\n_\\*نیازمندی‌های زمان کامپایل برنامه_\n\nاین فرمان‌ها را اجرا کنید:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n### شخصی سازی و تنظیمات\n\nاگر در حال حاضر از i3 استفاده می‌کنید، تنظیمات i3 خودتان را در فایل ‪`~/.config/sway/config`‬ کپی کنید و بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، فایل نمونه تنظیمات را استفاده کنید. این فایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید.\n\n## اجرا\n\nدر محیط TTY کافیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طرف sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد).\n"
  },
  {
    "path": "README.it.md",
    "content": "# sway\n\nsway è un compositore di [Wayland] compatibile con [i3]. Leggi le [FAQ].\nUnisciti al [canale IRC] \\(#sway su irc.libera.chat).\n\n## Firma delle versioni\n\nLe versioni sono firmate con la chiave [E88F5E48] e sono pubblicate\n[su GitHub][GitHub releases].\n\n## Installazione\n\n### Da un pacchetto\n\nSway è disponibile in molte distribuzioni, prova a installare il pacchetto\n\"sway\" per la tua.\n\n### Compilazione dei sorgenti\n\nConsulta [questa pagina del wiki][Development setup] se vuoi compilare l'HEAD\ndi sway e wlroots per testarli o contribuire allo sviluppo.\n\nInstalla le dipendenze:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (opzionale: area di notifica)\n* [scdoc] (opzionale: pagine del manuale) \\*\n* git (opzionale: informazioni sulla versione) \\*\n\n_\\* Dipendenza necessaria per la compilazione_\n\nEsegui questi comandi:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Configurazione\n\nSe hai già usato i3, copia il tuo file di configurazione in\n`~/.config/sway/config` e sway funzionerà immediatamente. Altrimenti, copia il\nfile d'esempio in `~/.config/sway/config`, generalmente è situato in\n`/etc/sway/config`. Consulta `man 5 sway` per ulteriori informazioni sulla\nconfigurazione.\n\n## Esecuzione\n\nLancia `sway` da un TTY o da un display manager.\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[canale IRC]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.ja.md",
    "content": "# sway\n\nSwayは[i3](https://i3wm.org/)互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。\n[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。\n[IRC チャンネル](https://web.libera.chat/gamja/?channels=#sway) (irc.libera.chatの#sway)もあります。\n\n[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)\n\n## 日本語サポート\n\nSirCmpwnは、日本語でのサポートをIRCとGitHubで行います。タイムゾーンはUTC-4です。\n\n## リリースの署名\n\nSwayのリリースは[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)で署名され、[GitHub](https://github.com/swaywm/sway/releases)で公開されています。\n\n## インストール\n\n### パッケージから\n\nSwayは沢山のディストリビューションで提供されています。\"sway\"パッケージのインストールを試してください。パッケージが存在しない場合は、[このページ](https://github.com/swaywm/sway/wiki/Unsupported-packages)で、あなたのディストリビューションでのインストールに関する情報を調べてください。\n\nあなたのディストリビューションにSwayのパッケージを提供したい場合は、SwayのIRCチャンネルを訪れるか、sir@cmpwn.comにメールを送り、相談してください。\n\n### ソースコードからコンパイル\n\n次の依存パッケージをインストールしてください:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (任意: システムイコンで必要です)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (任意: manで必要です) \\*\n* git (任意: バージョン情報で必要です) \\*\n\n_\\*コンパイル時の依存_\n\n次のコマンドを実行してください:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## 設定\n\n既にi3を使用している場合は、i3の設定ファイルを`~/.config/sway/config`にコピーすれば動きます。そうでない場合は、サンプルの設定ファイルを`~/.config/sway/config`にコピーしてください。サンプルの設定ファイルは、通常`/etc/sway/config`にあります。`man 5 sway`を実行することで、設定に関する情報を見ることができます。\n\n## 実行\n\n`sway`をTTYまたはディスプレイマネージャから実行してください。\n"
  },
  {
    "path": "README.ko.md",
    "content": "# sway\n\nsway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) 컴포지터입니다.\n[FAQ](https://github.com/swaywm/sway/wiki)를 읽어보세요. [IRC 채널](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)도 있습니다.\n\n## 릴리즈 서명\n\n릴리즈는 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)에서 서명되고,\n[GitHub에서](https://github.com/swaywm/sway/releases) 공개되고 있습니다.\n\n## 설치\n\n### 패키지를 통한 설치\n\nSway는 많은 배포판에서 이용할 수 있습니다. \"sway\" 패키지를 설치해 보세요.\n만약 없다면, [위키 페이지](https://github.com/swaywm/sway/wiki/Unsupported-packages)를 확인하세요.\n해당 배포판에 대한 설치 정보를 확인할 수 있습니다.\n\n당신의 배포판에 sway 패키지를 제공하고 싶다면,\nIRC 채널을 방문하거나 sir@cmpwn.com으로 이메일을 보내 상담 받으세요.\n\n### 소스를 통한 컴파일\n\n다음 의존 패키지들을 설치해 주세요:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (선택: system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (선택: man pages) \\*\n* git \\*\n\n_\\*컴파일 떄 필요_\n\n다음 명령을 실행하세요:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## 설정\n\ni3를 이미 사용 중이라면, i3 config을 `~/.config/sway/config`로 복사하세요.\n아니면, 샘플 구성 파일을 '~/.config/sway/config'에 복사할 수도 있습니다.\n일반적으로 \"/etc/sway/config\"에 위치해 있습니다.\n설정에 대한 정보를 보려면 \"man 5 sway\"를 실행하세요.\n\n## 실행\n\nTTY나 display manager에서 `sway`를 실행하세요.\n"
  },
  {
    "path": "README.md",
    "content": "# sway\n\n**[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]\n\nsway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the\n[IRC channel] \\(#sway on irc.libera.chat).\n\n## Release Signatures\n\nReleases are signed with [E88F5E48] and published [on GitHub][GitHub releases].\n\n## Installation\n\n### From Packages\n\nSway is available in many distributions. Try installing the \"sway\" package for\nyours.\n\n### Compiling from Source\n\nCheck out [this wiki page][Development setup] if you want to build the HEAD of\nsway and wlroots for testing or development.\n\nInstall dependencies:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (optional: additional image formats for system tray)\n* [swaybg] (optional: wallpaper)\n* [scdoc] (optional: man pages) \\*\n* git (optional: version info) \\*\n\n_\\* Compile-time dep_\n\nRun these commands:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Configuration\n\nIf you already use i3, then copy your i3 config to `~/.config/sway/config` and\nit'll work out of the box. Otherwise, copy the sample configuration file to\n`~/.config/sway/config`. It is usually located at `/etc/sway/config`.\nRun `man 5 sway` for information on the configuration.\n\n## Running\n\nRun `sway` from a TTY or from a display manager.\n\n[en]: https://github.com/swaywm/sway#readme\n[ar]: README.ar.md\n[az]: README.az.md\n[cs]: README.cs.md\n[de]: README.de.md\n[dk]: README.dk.md\n[es]: README.es.md\n[fr]: README.fr.md\n[ge]: README.ge.md\n[gr]: README.gr.md\n[hi]: README.hi.md\n[hu]: README.hu.md\n[ir]: README.ir.md\n[it]: README.it.md\n[ja]: README.ja.md\n[ko]: README.ko.md\n[nl]: README.nl.md\n[no]: README.no.md\n[pl]: README.pl.md\n[pt]: README.pt.md\n[ro]: README.ro.md\n[ru]: README.ru.md\n[sr]: README.sr.md\n[sv]: README.sv.md\n[tr]: README.tr.md\n[uk]: README.uk.md\n[zh-CN]: README.zh-CN.md\n[zh-TW]: README.zh-TW.md\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[swaybg]: https://github.com/swaywm/swaybg/\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.nl.md",
    "content": "# sway\n\nSway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor.\nLees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC\nkanaal](https://web.libera.chat/gamja/?channels=#sway) (#sway op\nirc.libera.chat).\n\n## Releasehandtekeningen\n\nReleases worden ondertekend met [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)\nen gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases).\n\n## Installatie\n\n### Via een pakket\n\nSway 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)\nvoor informatie over installatie in jouw distributie.\n\nAls je geïnteresseerd bent in het maken van pakketten voor je distributie, stuur een bericht in het IRC-\nkanaal of stuur een e-mail naar sir@cmpwn.com voor advies.\n\n### Compilatie vanuit broncode\n\nAfhankelijkheden installeren:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (optioneel: systeemtray)\n* [scdoc](https://git.sr.ht/~ircmpwn/scdoc) (optioneel: manpagina's) \\*\n* git \\*\n\n_\\* Compileerafhankelijkheden_\n\nVoer deze opdrachten uit:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Configuratie\n\nAls je al i3 gebruikt, kopieer dan je i3-configuratie naar `~/.config/sway/config` en\nhet zal zonder verdere configuratie werken. Kopieer anders het voorbeeldconfiguratiebestand naar\n`~/.config/sway/config`. Dit is meestal het bestand: `/etc/sway/config`.\nVoer `man 5 sway` uit voor informatie over het configureren van sway.\n\n## Uitvoeren\n\nVoer `sway` vanaf een TTY uit. Sommige display-managers kunnen werken, maar worden niet ondersteund door\nsway (van gdm is bekend dat het redelijk goed werkt).\n"
  },
  {
    "path": "README.no.md",
    "content": "# Sway\n\nSway er en [i3]-kompatibel [Wayland]-compositor. Les [Ofte stilte spørsmål].\nDelta på [IRC-kanalen][IRC-kanal] \\(#sway på irc.libera.chat).\n\n## Signaturer\n\nUtgivelser er signert med [E88F5E48] og publisert [på GitHub][GitHub releases].\n\n## Installasjon\n\n### Fra systempakker\n\nSway er tilgjengelig i mange distribusjoner. Prøv å installere pakken \"sway\"\nfra din distro sine repoer.\n\n### Kompilering fra kildekode\n\nSe [denne wiki-siden][Oppsetting for utvikling] hvis du vil bygge fra HEAD-grenen av\nsway og wlroots for testing eller utvikling.\n\nInstaller avhengigheter:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (valgfritt: støtte for ekstra bildeformater i system tray)\n* [swaybg] (valgfritt: bakgrunnsbilde)\n* [scdoc] (valgfritt: man pages) \\*\n* git (valgfritt: versjonsinformasjon) \\*\n\n_\\* Kompileringsavhengigheter_\n\nKjør følgende kommandoer:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Konfigurasjon\n\nHvis du allerede bruker i3 kan du bare kopiere din i3-konfigurasjon til\n`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurasjonsfilen til\n`~/.config/sway/config`. Eksempelfilen er normalt plasert i `/etc/sway/config`.\nKjør `man 5 sway` for å få opplysninger om konfigurasjonen.\n\n## Kjøring\n\nKjør `sway` fra en TTY eller fra en display manager.\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[Ofte stilte spørsmål]: https://github.com/swaywm/sway/wiki\n[IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Oppsetting for utvikling]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[swaybg]: https://github.com/swaywm/swaybg/\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.pl.md",
    "content": "# sway\n\nsway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/).\nPrzeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](https://web.libera.chat/gamja/?channels=#sway)\n(#sway na irc.libera.chat).\n\n## Podpisy cyfrowe wydań\n\nWydania są podpisywane przy pomocy klucza [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)\ni publikowane [na GitHubie](https://github.com/swaywm/sway/releases).\n\n## Instalacja\n\n### Z pakietów\n\nSway jest dostępny w wielu dystybucjach. Spróbuj zainstalować pakiet \"sway\" w swoim\nmenedżerze pakietów. Jeśli nie jest dostępny, sprawdź [tę stronę wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages)\naby uzyskać informacje dotyczące instalacji w swojej dystrybucji.\n\nJeśli chciałbyś stworzyć pakiet sway dla swojej dystrybucji, odwiedź kanał IRC lub wyślij email na\nadres sir@cmpwn.com w celu uzyskania wskazówek.\n\n### Kompilacja ze Źródła\n\nZainstaluj zależności:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (opcjonalnie: system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opcjonalnie: strony pomocy man) \\*\n* git \\*\n\n_\\*zależności kompilacji_\n\nWykonaj następujące polecenia:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Konfiguracja\n\nJeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i\nzadziała od ręki. W przeciwnym razie skopiuj przykładowy plik konfiguracyjny do folderu\n`~/.config/sway/config`; zazwyczaj znajduje się w `/etc/sway/config`.\nWykonaj polecenie `man 5 sway` aby uzyskać informacje dotyczące konfiguracji.\n\n## Uruchamianie\n\nWykonaj polecenie `sway` z poziomu TTY lub menedżera wyświetlania.\n"
  },
  {
    "path": "README.pt.md",
    "content": "# sway\n\nO sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatível com o [i3](https://i3wm.org/).\nLeia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do    \nIRC](https://web.libera.chat/gamja/?channels=#sway) (#sway em\nirc.libera.chat).\n\n## Assinatura das versões\n\nAs versões são assinadas com [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)\ne publicadas [no GitHub](https://github.com/swaywm/sway/releases).\n\n## Instalação\n\n### A partir de pacotes\nO Sway está disponível em várias distribuições. Tente instalar o pacote \"sway\"\nna sua. Caso não esteja disponível, verifique [esta wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages)\npara se informar a sobre a instalação para sua distribuição.\n\nSe você está interessado em criar um pacote do sway para a sua distribuição, verifique canal do IRC\nou mande um email para sir@cmpwn.com para obter informações.\n\n### Compilando a partir do código-fonte\n\nVerifique [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.\n\nInstale as dependências:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (opcional: system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opcional: man pages) \\*\n* git (opcional: informações de versão) \\*\n\n_\\*Dependência de tempo de compilação_\n\nExecute esses comandos:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Configuração\n\nSe você já utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e\ntudo funcionará normalmente. Caso contrário, copie o arquivo de configuração de exemplo para\n`~/.config/sway/config`. Normalmente, este arquivo está localizado em `/etc/sway/config`.\nExecute `man 5 sway` para se informar sobre a configuração.\n\n## Execução\n\nExecute o comando `sway` de um TTY. Alguns gerenciadores de display (ou gerenciadores de login) podem funcionar mas alguns não são suportados\npelo sway (o gdm é conhecido por funcionar bem).\n"
  },
  {
    "path": "README.ro.md",
    "content": "# sway\n\nsway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/).\nCitiț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).\n\n## Semnarea digitală\n\nNoile versiuni sunt semnate cu [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)\nși postate [pe GitHub](https://github.com/swaywm/sway/releases).\n\n## Instalare\n\n### Din pachete (packages) \n\nsway 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)\npentru informații a cum puteți să instalați pentru distribuția voastră.\n\nDacă 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.\n\n### Compilare din sursă\n\nDependențe pentru instalare:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (opțional, dacă doriți să aveți system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opțional, pentru paginile man) \\*\n* git (opțional, pentru informații de versiune) \\*\n\n*Dependențe doar pentru compilare*\n\nRulați aceste comenzi:\n\n```\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n```\n\n## Configurare\n\nDacă 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`.\nFolosiți comanda `man 5 sway` pentru informații despre configurare.\n\n## Lansare\n\nFolosiț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).\n"
  },
  {
    "path": "README.ru.md",
    "content": "# sway\n\nsway - это [i3]-совместимый композитор [Wayland].\nБольше информации в [FAQ]. Присоединяйтесь к\n[IRC-каналу][IRC channel] (#sway на\nirc.libera.chat).\n\n## Подписи релизов\n\nРелизы подписываются ключом [E88F5E48] и публикуются [на GitHub][GitHub releases].\n\n## Установка\n\n### Из репозиториев\n\nSway доступен во многих дистрибутивах. Попробуйте установить пакет \"sway\".\n\nЕсли вас интересует создание пакета sway для вашего дистрибутива, зайдите на [IRC-канал][IRC channel]\nили отправьте письмо на sir@cmpwn.com за советом.\n\n### Сборка из исходников\n\nПосетите [эту страницу на вики][Development setup], если вы хотите построить последнюю версию\nsway и wlroots для тестирования или разработки. \n\nУстановите зависимости:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (опционально: для работы трея)\n* [scdoc] (опционально: для man-страниц) \\*\n* git (опционально: для информации о версии) \\*\n\n_\\*Зависимости для сборки_\n\nВыполните эти команды:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Настройка\n\nЕсли вы уже используете i3, скопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и\nон сразу же заработает. В противном случае, скопируйте образец конфигурационного файла в\n`~/.config/sway/config`. Обычно он располагается в `/etc/sway/config`.\nЗапустите `man 5 sway` для изучения информации о настройке.\n\n## Запуск\n\nВыполните команду `sway` прямо из TTY или дисплейного менеджера.\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.sr.md",
    "content": "# sway\n\nsway је [i3]-компатибилан [Wayland] композитор. Прочитајте [FAQ]. Придружите се\n[IRC каналу] \\(#sway на irc.libera.chat).\n\n## Потписи Издања\n\nИздања су потписана са [E88F5E48] и објављена [на GitHub-у][GitHub releases].\n\n## Инсталација\n\n### Из пакета\n\nSway је доступан у многим дистрибуцијама. Покушајте да инсталирате \"sway\" пакет за\nвашу.\n\n### Компајлирање из Извора\n\nПогледајте [ову вики страницу][Development setup], ако желите да компајлирате HEAD верзију\nsway-а и wlroots-а за тестирање или развој.\n\nИнсталирајте зависности:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (опционо: додатни формати слика за системску траку)\n* [swaybg] (опционо: позадина)\n* [scdoc] (опционо: man странице) \\*\n* git (опционо: информације о верзији) \\*\n\n_\\* Потребно само за компајлирање_\n\nПокрените следеће команде:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Конфигурација\n\nАко већ користите i3, копирајте вашу i3 конфигурацију у `~/.config/sway/config` и\nрадиће одмах. У супротном, копирајте пример конфигурационе датотеке у\n`~/.config/sway/config`. Обично се налази у `/etc/sway/config`.\nПокрените `man 5 sway` за информације о конфигурацији.\n\n## Покретање\n\nПокрените `sway` из TTY-a или из менаџера приказа.\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC каналу]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[swaybg]: https://github.com/swaywm/swaybg/\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.sv.md",
    "content": "# sway\n\nsway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår\n[IRC-kanal] \\(#sway på irc.libera.chat).\n\n## Utgåvosignaturer\n\nUtgåvor är signerade med [E88F5E48] och publicerade på [GitHub][GitHub releases].\n\n## Installering\n\n### Med pakethanterare\n\nSway är tillgänglig i många distributioner. Prova att installera \"sway\" med din distributions pakethanterare.\n\n### Genom att kompilera från källkod\n\nKolla in [denna wiki-sida][Development setup] om du vill bygga sway och wlroots HEAD för testning eller utveckling.\n\nInstallera paket som sway behöver:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (valbar: systembricka)\n* [scdoc] (valbar: manualer) \\*\n* git (valbar: versioninfo) \\*\n\n_\\* Krav för kompilering_\n\nKör dessa kommandon:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## Konfiguration\n\nIfall du redan använder i3 så kan du kopiera din konfigurationsfil till `~/.config/sway/config` och det kommer då att fungera som det ska.\nKopiera annars exemplarkonfigurationsfilen till `~/.config/sway/config`. Den ligger oftast i `/etc/sway/config`.\nKör `man 5 sway` för mer information kring konfigurationen.\n\n## Att köra sway\n\nKör `sway` från en TTY. Vissa inloggningahanterare kan fungera men inte vara stödda av sway (gdm ska fungera hyfsat bra).\n\n[en]: https://github.com/swaywm/sway#readme\n[de]: README.de.md\n[dk]: README.dk.md\n[es]: README.es.md\n[fr]: README.fr.md\n[sv]: README.sv.md\n[gr]: README.gr.md\n[hu]: README.hu.md\n[ir]: README.ir.md\n[it]: README.it.md\n[ja]: README.ja.md\n[ko]: README.ko.md\n[nl]: README.nl.md\n[pl]: README.pl.md\n[pt]: README.pt.md\n[ro]: README.ro.md\n[ru]: README.ru.md\n[tr]: README.tr.md\n[uk]: README.uk.md\n[zh-CN]: README.zh-CN.md\n[zh-TW]: README.zh-TW.md\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.tr.md",
    "content": "# sway\n\n\nSway, [i3]-uyumlu bir [Wayland] dizgicisidir. [SSS][FAQ]'yi okuyun. \n[IRC kanalı][IRC channel]na katılın \\(irc.libera.chat'te #sway (İngilizce)).\n\n## Sürüm imzaları\n\nSürümler [E88F5E48] ile imzalandı ve [GitHub][GitHub releases]'da yayınlandı.\n\n## Kurulum\n\n### Paketler ile\n\nSway birçok dağıtımda mevcuttur. Sizinki için \"sway\" paketini yüklemeyi deneyin.\n\nDağı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.\n\n### Kaynak koddan derleme\n\nTest 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.\n\nAşağıdaki bağımlılıkları yükleyin:\n\n* meson \\*\n* [wlroots]\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (isteğe bağlı: system tray)\n* [scdoc] (isteğe bağlı: man pages) \\*\n* git (isteğe bağlı: version info) \\*\n\n_\\*Derleme-anı bağımlılıkları_\n\nŞu komutları çalıştırın:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Yapılandırma\n\nZaten 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.\nYapılandırma hakkında bilgi almak için `man 5 sway` komutunu çalıştırın.\n\n## Çalıştırma\n\nTTY'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.)\n\n[i3]: https://i3wm.org/\n[Wayland]: http://wayland.freedesktop.org/\n[FAQ]: https://github.com/swaywm/sway/wiki\n[IRC channel]: https://web.libera.chat/gamja/?channels=#sway\n[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48\n[GitHub releases]: https://github.com/swaywm/sway/releases\n[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup\n[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots\n[scdoc]: https://git.sr.ht/~sircmpwn/scdoc\n"
  },
  {
    "path": "README.uk.md",
    "content": "# sway\n\nSway це сумісний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/).\nОзнайомтесь з [ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в\nIRC](https://web.libera.chat/gamja/?channels=#sway) (#sway на\nirc.libera.chat).\n\n## Підтримка українською мовою\n\nЯкщо ви хочете отримати підтримку українською мовою, можете звернутись до користувача\nHummer12007 у IRC-спільноті. Будьте терплячі, вам обов'язково допоможуть.\n\nНаразі переклад Sway українською ще не завершено (він неповний), проте у вас є шанс долучитись,\nдетальніше див. [статус](https://github.com/swaywm/sway/issues/1318#issuecomment-322277382).\n\n## Підписи випусків\n\nВипуски підписані ключем [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)\nта публікуються на сторінці [GitHub](https://github.com/swaywm/sway/releases).\n\n## Встановлення\n\n### З пакунків\n\nSway доступний у багатьох дистрибутивах Linux (а також у FreeBSD).\nСпробуйте встановити пакунок `sway` у вашому.\nЯкщо він недоступний, перегляньте цю [сторінку Wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages)\nдля інформації щодо встановлення на вашому дистрибутиві.\n\nЯкщо ви готові та зацікавлені запакувати і підтримувати Sway у вашому\nдистрибутиві, звертайтесь за порадами до нашого каналу в IRC або\nпишіть на електронну пошту [sir@cmpwn.com](mailto:sir@cmpwn.com).\n\n### З вихідного коду\n\nВстановіть залежності:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (optional: system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (необов'язково, необхідно для сторінок man) \\*\n* git \\*\n\n_\\*Лише для компіляції_\n\nВиконайте ці команди:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## Налаштування\n\nЯкщо ви вже використовуєте i3, скопіюйте свій файл налаштувань\nдо `~/.config/sway/config`, він має запрацювати. Інакше, скопіюйте\nтуди файл-зразок (зазвичай знаходиться у `/etc/sway/config`), і налаштуйте під себе.\n\nБільше інформації щодо налаштувань можете знайти, виконавши `man 5 sway`.\n\n## Запуск\n\nВиконайте `sway` у TTY. Деякі дисплейні менеджери (менеджери сеансу/стільниць)\nможуть працювати, але офіційно не підтримуються (проте сумісніть із gdm достатньо висока).\n"
  },
  {
    "path": "README.zh-CN.md",
    "content": "# sway\n\nsway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor。\n [查看FAQ](https://github.com/swaywm/sway/wiki)/ [加入IRC频道](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)\n\n## 发行签名\n\n每个发行版都以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 的密钥签名并发布在 [GitHub](https://github.com/swaywm/sway/releases)上。\n\n## 安装\n\n### 从包管理器安装\n\nSway 在很多发行版中可用。请尝试在你的发行版中安装 `sway` 。\n\n### 从源码编译\n\n如果想要构建最新版sway和wlroots用以测试和开发，请查看 [此wiki页面](https://github.com/swaywm/sway/wiki/Development-Setup)\n\n安装如下依赖:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (可选的: system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (可选: man pages) \\*\n* git \\*\n\n_\\*编译时依赖_\n\n运行如下命令:\n\n    meson setup build/\n    ninja -C build/\n    sudo ninja -C build/ install\n\n## 配置\n\n如果你已经在使用i3，直接复制i3配置文件到 `~/.config/sway/config`，这是开箱即用的。或者，你可以复制配置样例到`~/.config/sway/config`。它通常位于 `/etc/sway/config`。\n运行 `man 5 sway` 获取关于配置的更多信息。\n\n## 运行\n\n从 TTY 中运行 `sway`。 某些显示管理器（Display Manager）也许可以工作但不被 sway 支持。\n(已知 gdm 工作得非常好)。\n"
  },
  {
    "path": "README.zh-TW.md",
    "content": "# sway\n\nsway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。\n閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 加入 [IRC\n頻道](https://web.libera.chat/gamja/?channels=#sway) (#sway on\nirc.libera.chat)\n\n## 發行簽章\n\n所有發行的版本都會以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 簽署\n並發佈於 [GitHub](https://github.com/swaywm/sway/releases)\n\n## 安裝\n\n### 從套件安裝\n\nSway 在許多發行版都有提供。請自己嘗試於你的發行版安裝 「sway」這個套件。\n如果無法取得，請查看 [這個 wiki 頁面](https://github.com/swaywm/sway/wiki/Unsupported-packages)\n以取得更多關於如何於你使用的發行版上安裝的資訊。\n\n如果你想要為你使用的發行版包裝 sway，請到 IRC 頻道或是直接寄封信到 sir@cmpwn.com 來取得一些建議。\n\n### 從原始碼編譯\n\n相依套件:\n\n* meson \\*\n* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)\n* wayland\n* wayland-protocols \\*\n* pcre2\n* json-c\n* pango\n* cairo\n* gdk-pixbuf2 (選擇性: system tray)\n* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (選擇性: man pages) \\*\n* git \\*\n\n_\\*編譯時相依_\n\n執行這些指令:\n\n    meson setup build\n    ninja -C build\n    sudo ninja -C build install\n\n## 設定檔\n\n如果你已經在使用 i3，你可以直接將你的 i3 設定檔複製到 `~/.config/sway/config` 然後就能直接使用。\n或者你也可以把範例設定檔複製到 `~/.config/sway/config`。 它通常會在 `/etc/sway/config`。\n執行 `man 5 sway` 來取得更多關於設定檔的資訊。\n\n## 執行\n\n在 TTY 執行 `sway`。有些 display manager 可能可以運作但 sway 不提供支援 (已知 gdm 運作的很好)\n"
  },
  {
    "path": "assets/LICENSE",
    "content": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n    INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n    HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n  i. the right to reproduce, adapt, distribute, perform, display,\n     communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n     likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n     subject to the limitations in paragraph 4(a), below;\n  v. rights protecting the extraction, dissemination, use and reuse of data\n     in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n     European Parliament and of the Council of 11 March 1996 on the legal\n     protection of databases, and under any national implementation\n     thereof, including any amended or successor version of such\n     directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n     world based on applicable law or treaty, and any national\n     implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n    surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n    warranties of any kind concerning the Work, express, implied,\n    statutory or otherwise, including without limitation warranties of\n    title, merchantability, fitness for a particular purpose, non\n    infringement, or the absence of latent or other defects, accuracy, or\n    the present or absence of errors, whether or not discoverable, all to\n    the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n    that may apply to the Work or any use thereof, including without\n    limitation any person's Copyright and Related Rights in the Work.\n    Further, Affirmer disclaims responsibility for obtaining any necessary\n    consents, permissions or other rights required for any use of the\n    Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n    party to this document and has no duty or obligation with respect to\n    this CC0 or use of the Work.\n"
  },
  {
    "path": "client/meson.build",
    "content": "lib_sway_client = static_library(\n\t'sway-client',\n\tfiles(\n\t\t'pool-buffer.c',\n\t),\n\tdependencies: [\n\t\tcairo,\n\t\tpango,\n\t\tpangocairo,\n\t\twayland_client\n\t],\n\tlink_with: [lib_sway_common],\n\tinclude_directories: sway_inc\n)\n"
  },
  {
    "path": "client/pool-buffer.c",
    "content": "#include <assert.h>\n#include <cairo.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <pango/pangocairo.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <time.h>\n#include <unistd.h>\n#include <wayland-client.h>\n#include \"config.h\"\n#include \"pool-buffer.h\"\n#include \"util.h\"\n\nstatic int anonymous_shm_open(void) {\n\tint retries = 100;\n\n\tdo {\n\t\t// try a probably-unique name\n\t\tstruct timespec ts;\n\t\tclock_gettime(CLOCK_MONOTONIC, &ts);\n\t\tpid_t pid = getpid();\n\t\tchar name[50];\n\t\tsnprintf(name, sizeof(name), \"/sway-%x-%x\",\n\t\t\t(unsigned int)pid, (unsigned int)ts.tv_nsec);\n\n\t\t// shm_open guarantees that O_CLOEXEC is set\n\t\tint fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);\n\t\tif (fd >= 0) {\n\t\t\tshm_unlink(name);\n\t\t\treturn fd;\n\t\t}\n\n\t\t--retries;\n\t} while (retries > 0 && errno == EEXIST);\n\n\treturn -1;\n}\n\nstatic void buffer_release(void *data, struct wl_buffer *wl_buffer) {\n\tstruct pool_buffer *buffer = data;\n\tbuffer->busy = false;\n}\n\nstatic const struct wl_buffer_listener buffer_listener = {\n\t.release = buffer_release\n};\n\nstatic struct pool_buffer *create_buffer(struct wl_shm *shm,\n\t\tstruct pool_buffer *buf, int32_t width, int32_t height,\n\t\tuint32_t format) {\n\tuint32_t stride = width * 4;\n\tsize_t size = stride * height;\n\n\tint fd = anonymous_shm_open();\n\tif (fd == -1) {\n\t\treturn NULL;\n\t}\n\tif (ftruncate(fd, size) < 0) {\n\t\tclose(fd);\n\t\treturn NULL;\n\t}\n\tvoid *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n\tstruct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);\n\tbuf->buffer = wl_shm_pool_create_buffer(pool, 0,\n\t\t\twidth, height, stride, format);\n\twl_shm_pool_destroy(pool);\n\tclose(fd);\n\n\tbuf->size = size;\n\tbuf->width = width;\n\tbuf->height = height;\n\tbuf->data = data;\n\tbuf->surface = cairo_image_surface_create_for_data(data,\n\t\t\tCAIRO_FORMAT_ARGB32, width, height, stride);\n\tbuf->cairo = cairo_create(buf->surface);\n\tbuf->pango = pango_cairo_create_context(buf->cairo);\n\n\twl_buffer_add_listener(buf->buffer, &buffer_listener, buf);\n\treturn buf;\n}\n\nvoid destroy_buffer(struct pool_buffer *buffer) {\n\tif (buffer->buffer) {\n\t\twl_buffer_destroy(buffer->buffer);\n\t}\n\tif (buffer->cairo) {\n\t\tcairo_destroy(buffer->cairo);\n\t}\n\tif (buffer->surface) {\n\t\tcairo_surface_destroy(buffer->surface);\n\t}\n\tif (buffer->pango) {\n\t\tg_object_unref(buffer->pango);\n\t}\n\tif (buffer->data) {\n\t\tmunmap(buffer->data, buffer->size);\n\t}\n\tmemset(buffer, 0, sizeof(struct pool_buffer));\n}\n\nstruct pool_buffer *get_next_buffer(struct wl_shm *shm,\n\t\tstruct pool_buffer pool[static 2], uint32_t width, uint32_t height) {\n\tstruct pool_buffer *buffer = NULL;\n\n\tfor (size_t i = 0; i < 2; ++i) {\n\t\tif (pool[i].busy) {\n\t\t\tcontinue;\n\t\t}\n\t\tbuffer = &pool[i];\n\t}\n\n\tif (!buffer) {\n\t\treturn NULL;\n\t}\n\n\tif (buffer->width != width || buffer->height != height) {\n\t\tdestroy_buffer(buffer);\n\t}\n\n\tif (!buffer->buffer) {\n\t\tif (!create_buffer(shm, buffer, width, height,\n\t\t\t\t\tWL_SHM_FORMAT_ARGB8888)) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tbuffer->busy = true;\n\treturn buffer;\n}\n"
  },
  {
    "path": "common/cairo.c",
    "content": "#include <stdint.h>\n#include <cairo.h>\n#include \"cairo_util.h\"\n\nvoid cairo_set_source_u32(cairo_t *cairo, uint32_t color) {\n\tcairo_set_source_rgba(cairo,\n\t\t\t(color >> (3*8) & 0xFF) / 255.0,\n\t\t\t(color >> (2*8) & 0xFF) / 255.0,\n\t\t\t(color >> (1*8) & 0xFF) / 255.0,\n\t\t\t(color >> (0*8) & 0xFF) / 255.0);\n}\n\ncairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) {\n\tswitch (subpixel) {\n\tcase WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:\n\t\treturn CAIRO_SUBPIXEL_ORDER_RGB;\n\tcase WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:\n\t\treturn CAIRO_SUBPIXEL_ORDER_BGR;\n\tcase WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:\n\t\treturn CAIRO_SUBPIXEL_ORDER_VRGB;\n\tcase WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:\n\t\treturn CAIRO_SUBPIXEL_ORDER_VBGR;\n\tdefault:\n\t\treturn CAIRO_SUBPIXEL_ORDER_DEFAULT;\n\t}\n\treturn CAIRO_SUBPIXEL_ORDER_DEFAULT;\n}\n\ncairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,\n\t\tint width, int height) {\n\tint image_width = cairo_image_surface_get_width(image);\n\tint image_height = cairo_image_surface_get_height(image);\n\n\tcairo_surface_t *new =\n\t\tcairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);\n\tcairo_t *cairo = cairo_create(new);\n\tcairo_scale(cairo, (double)width / image_width,\n\t\t\t(double)height / image_height);\n\tcairo_set_source_surface(cairo, image, 0, 0);\n\n\tcairo_paint(cairo);\n\tcairo_destroy(cairo);\n\treturn new;\n}\n"
  },
  {
    "path": "common/gesture.c",
    "content": "#include \"gesture.h\"\n\n#include <math.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nconst uint8_t GESTURE_FINGERS_ANY = 0;\n\nchar *gesture_parse(const char *input, struct gesture *output) {\n\t// Clear output in case of failure\n\toutput->type = GESTURE_TYPE_NONE;\n\toutput->fingers = GESTURE_FINGERS_ANY;\n\toutput->directions = GESTURE_DIRECTION_NONE;\n\n\t// Split input type, fingers and directions\n\tlist_t *split = split_string(input, \":\");\n\tif (split->length < 1 || split->length > 3) {\n\t\treturn format_str(\n\t\t\t\t\"expected <gesture>[:<fingers>][:direction], got %s\",\n\t\t\t\tinput);\n\t}\n\n\t// Parse gesture type\n\tif (strcmp(split->items[0], \"hold\") == 0) {\n\t\toutput->type = GESTURE_TYPE_HOLD;\n\t} else if (strcmp(split->items[0], \"pinch\") == 0) {\n\t\toutput->type = GESTURE_TYPE_PINCH;\n\t} else if (strcmp(split->items[0], \"swipe\") == 0) {\n\t\toutput->type = GESTURE_TYPE_SWIPE;\n\t} else {\n\t\treturn format_str(\"expected hold|pinch|swipe, got %s\",\n\t\t\t\t(const char *)split->items[0]);\n\t}\n\n\t// Parse optional arguments\n\tif (split->length > 1) {\n\t\tchar *next = split->items[1];\n\n\t\t// Try to parse as finger count (1-9)\n\t\tif (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {\n\t\t\toutput->fingers = atoi(next);\n\n\t\t\t// Move to next if available\n\t\t\tnext = split->length == 3 ? split->items[2] : NULL;\n\t\t} else if (split->length == 3) {\n\t\t\t// Fail here if argument can only be finger count\n\t\t\treturn format_str(\"expected 1-9, got %s\", next);\n\t\t}\n\n\t\t// If there is an argument left, try to parse as direction\n\t\tif (next) {\n\t\t\tlist_t *directions = split_string(next, \"+\");\n\n\t\t\tfor (int i = 0; i < directions->length; ++i) {\n\t\t\t\tconst char *item = directions->items[i];\n\t\t\t\tif (strcmp(item, \"any\") == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t} else if (strcmp(item, \"up\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_UP;\n\t\t\t\t} else if (strcmp(item, \"down\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_DOWN;\n\t\t\t\t} else if (strcmp(item, \"left\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_LEFT;\n\t\t\t\t} else if (strcmp(item, \"right\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_RIGHT;\n\t\t\t\t} else if (strcmp(item, \"inward\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_INWARD;\n\t\t\t\t} else if (strcmp(item, \"outward\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_OUTWARD;\n\t\t\t\t} else if (strcmp(item, \"clockwise\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_CLOCKWISE;\n\t\t\t\t} else if (strcmp(item, \"counterclockwise\") == 0) {\n\t\t\t\t\toutput->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;\n\t\t\t\t} else {\n\t\t\t\t\treturn format_str(\"expected direction, got %s\", item);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlist_free_items_and_destroy(directions);\n\t\t}\n\t} // if optional args\n\n\tlist_free_items_and_destroy(split);\n\n\treturn NULL;\n}\n\nconst char *gesture_type_string(enum gesture_type type) {\n\tswitch (type) {\n\tcase GESTURE_TYPE_NONE:\n\t\treturn \"none\";\n\tcase GESTURE_TYPE_HOLD:\n\t\treturn \"hold\";\n\tcase GESTURE_TYPE_PINCH:\n\t\treturn \"pinch\";\n\tcase GESTURE_TYPE_SWIPE:\n\t\treturn \"swipe\";\n\t}\n\n\treturn NULL;\n}\n\nconst char *gesture_direction_string(enum gesture_direction direction) {\n\tswitch (direction) {\n\tcase GESTURE_DIRECTION_NONE:\n\t\treturn \"none\";\n\tcase GESTURE_DIRECTION_UP:\n\t\treturn \"up\";\n\tcase GESTURE_DIRECTION_DOWN:\n\t\treturn \"down\";\n\tcase GESTURE_DIRECTION_LEFT:\n\t\treturn \"left\";\n\tcase GESTURE_DIRECTION_RIGHT:\n\t\treturn \"right\";\n\tcase GESTURE_DIRECTION_INWARD:\n\t\treturn \"inward\";\n\tcase GESTURE_DIRECTION_OUTWARD:\n\t\treturn \"outward\";\n\tcase GESTURE_DIRECTION_CLOCKWISE:\n\t\treturn \"clockwise\";\n\tcase GESTURE_DIRECTION_COUNTERCLOCKWISE:\n\t\treturn \"counterclockwise\";\n\t}\n\n\treturn NULL;\n}\n\n// Helper to turn combination of directions flags into string representation.\nstatic char *gesture_directions_to_string(uint32_t directions) {\n\tchar *result = NULL;\n\n\tfor (uint8_t bit = 0; bit < 32; bit++) {\n\t\tuint32_t masked = directions & (1 << bit);\n\t\tif (masked) {\n\t\t\tconst char *name = gesture_direction_string(masked);\n\n\t\t\tif (!name) {\n\t\t\t\tname = \"unknown\";\n\t\t\t}\n\n\t\t\tif (!result) {\n\t\t\t\tresult = strdup(name);\n\t\t\t} else {\n\t\t\t\tchar *new = format_str(\"%s+%s\", result, name);\n\t\t\t\tfree(result);\n\t\t\t\tresult = new;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(!result) {\n\t\treturn strdup(\"any\");\n\t}\n\n\treturn result;\n}\n\nchar *gesture_to_string(struct gesture *gesture) {\n\tchar *directions = gesture_directions_to_string(gesture->directions);\n\tchar *result = format_str(\"%s:%u:%s\",\n\t\tgesture_type_string(gesture->type),\n\t\tgesture->fingers, directions);\n\tfree(directions);\n\treturn result;\n}\n\nbool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) {\n\t// Check that gesture type matches\n\tif (target->type != type) {\n\t\treturn false;\n\t}\n\n\t// Check that finger count matches\n\tif (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) {\n\t// Check type and fingers\n\tif (!gesture_check(target, to_match->type, to_match->fingers)) {\n\t\treturn false;\n\t}\n\n\t// Enforce exact matches ...\n\tif (exact && target->directions != to_match->directions) {\n\t\treturn false;\n\t}\n\n\t// ... or ensure all target directions are matched\n\treturn (target->directions & to_match->directions) == target->directions;\n}\n\nbool gesture_equal(struct gesture *a, struct gesture *b) {\n\treturn a->type == b->type\n\t\t&& a->fingers == b->fingers\n\t\t&& a->directions == b->directions;\n}\n\n// Return count of set bits in directions bit field.\nstatic uint8_t gesture_directions_count(uint32_t directions) {\n\tuint8_t count = 0;\n\tfor (; directions; directions >>= 1) {\n\t\tcount += directions & 1;\n\t}\n\treturn count;\n}\n\n// Compare direction bit count of two direction.\nstatic int8_t gesture_directions_compare(uint32_t a, uint32_t b) {\n\treturn gesture_directions_count(a) - gesture_directions_count(b);\n}\n\n// Compare two direction based on their direction bit count\nint8_t gesture_compare(struct gesture *a, struct gesture *b) {\n\treturn gesture_directions_compare(a->directions, b->directions);\n}\n\nvoid gesture_tracker_begin(struct gesture_tracker *tracker,\n\t\tenum gesture_type type, uint8_t fingers) {\n\ttracker->type = type;\n\ttracker->fingers = fingers;\n\n\ttracker->dx = 0.0;\n\ttracker->dy = 0.0;\n\ttracker->scale = 1.0;\n\ttracker->rotation = 0.0;\n\n\tsway_log(SWAY_DEBUG, \"begin tracking %s:%u:? gesture\",\n\t\tgesture_type_string(type), fingers);\n}\n\nbool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) {\n\treturn tracker->type == type;\n}\n\nvoid gesture_tracker_update(struct gesture_tracker *tracker,\n\t\tdouble dx, double dy, double scale, double rotation) {\n\tif (tracker->type == GESTURE_TYPE_HOLD) {\n\t\tsway_assert(false, \"hold does not update.\");\n\t\treturn;\n\t}\n\n\ttracker->dx += dx;\n\ttracker->dy += dy;\n\n\tif (tracker->type == GESTURE_TYPE_PINCH) {\n\t\ttracker->scale = scale;\n\t\ttracker->rotation += rotation;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"update tracking %s:%u:? gesture: %f %f %f %f\",\n\t\t\tgesture_type_string(tracker->type),\n\t\t\ttracker->fingers,\n\t\t\ttracker->dx, tracker->dy,\n\t\t\ttracker->scale, tracker->rotation);\n}\n\nvoid gesture_tracker_cancel(struct gesture_tracker *tracker) {\n\tsway_log(SWAY_DEBUG, \"cancel tracking %s:%u:? gesture\",\n\t\t\tgesture_type_string(tracker->type), tracker->fingers);\n\n\ttracker->type = GESTURE_TYPE_NONE;\n}\n\nstruct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {\n\tstruct gesture *result = calloc(1, sizeof(struct gesture));\n\n\t// Ignore gesture under some threshold\n\t// TODO: Make configurable\n\tconst double min_rotation = 5;\n\tconst double min_scale_delta = 0.1;\n\n\t// Determine direction\n\tswitch(tracker->type) {\n\t// Gestures with scale and rotation\n\tcase GESTURE_TYPE_PINCH:\n\t\tif (tracker->rotation > min_rotation) {\n\t\t\tresult->directions |= GESTURE_DIRECTION_CLOCKWISE;\n\t\t}\n\t\tif (tracker->rotation < -min_rotation) {\n\t\t\tresult->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;\n\t\t}\n\n\t\tif (tracker->scale > (1.0 + min_scale_delta)) {\n\t\t\tresult->directions |= GESTURE_DIRECTION_OUTWARD;\n\t\t}\n\t\tif (tracker->scale < (1.0 - min_scale_delta)) {\n\t\t\tresult->directions |= GESTURE_DIRECTION_INWARD;\n\t\t}\n\t\t__attribute__ ((fallthrough));\n\t// Gestures with dx and dy\n\tcase GESTURE_TYPE_SWIPE:\n\t\tif (fabs(tracker->dx) > fabs(tracker->dy)) {\n\t\t\tif (tracker->dx > 0) {\n\t\t\t\tresult->directions |= GESTURE_DIRECTION_RIGHT;\n\t\t\t} else {\n\t\t\t\tresult->directions |= GESTURE_DIRECTION_LEFT;\n\t\t\t}\n\t\t} else {\n\t\t\tif (tracker->dy > 0) {\n\t\t\t\tresult->directions |= GESTURE_DIRECTION_DOWN;\n\t\t\t} else {\n\t\t\t\tresult->directions |= GESTURE_DIRECTION_UP;\n\t\t\t}\n\t\t}\n\t// Gesture without any direction\n\tcase GESTURE_TYPE_HOLD:\n\t\tbreak;\n\t// Not tracking any gesture\n\tcase GESTURE_TYPE_NONE:\n\t\tsway_assert(false, \"Not tracking any gesture.\");\n\t\treturn result;\n\t}\n\n\tresult->type = tracker->type;\n\tresult->fingers = tracker->fingers;\n\n\tchar *description = gesture_to_string(result);\n\tsway_log(SWAY_DEBUG, \"end tracking gesture: %s\", description);\n\tfree(description);\n\n\ttracker->type = GESTURE_TYPE_NONE;\n\n\treturn result;\n}\n"
  },
  {
    "path": "common/ipc-client.c",
    "content": "#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n#include \"ipc-client.h\"\n#include \"log.h\"\n\nstatic const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};\n\n#define IPC_HEADER_SIZE (sizeof(ipc_magic) + 8)\n\nchar *get_socketpath(void) {\n\tconst char *swaysock = getenv(\"SWAYSOCK\");\n\tif (swaysock) {\n\t\treturn strdup(swaysock);\n\t}\n\tchar *line = NULL;\n\tsize_t line_size = 0;\n\tFILE *fp = popen(\"sway --get-socketpath 2>/dev/null\", \"r\");\n\tif (fp) {\n\t\tssize_t nret = getline(&line, &line_size, fp);\n\t\tpclose(fp);\n\t\tif (nret > 0) {\n\t\t\t// remove trailing newline, if there is one\n\t\t\tif (line[nret - 1] == '\\n') {\n\t\t\t\tline[nret - 1] = '\\0';\n\t\t\t}\n\t\t\treturn line;\n\t\t}\n\t}\n\tconst char *i3sock = getenv(\"I3SOCK\");\n\tif (i3sock) {\n\t\tfree(line);\n\t\treturn strdup(i3sock);\n\t}\n\tfp = popen(\"i3 --get-socketpath 2>/dev/null\", \"r\");\n\tif (fp) {\n\t\tssize_t nret = getline(&line, &line_size, fp);\n\t\tpclose(fp);\n\t\tif (nret > 0) {\n\t\t\t// remove trailing newline, if there is one\n\t\t\tif (line[nret - 1] == '\\n') {\n\t\t\t\tline[nret - 1] = '\\0';\n\t\t\t}\n\t\t\treturn line;\n\t\t}\n\t}\n\tfree(line);\n\treturn NULL;\n}\n\nint ipc_open_socket(const char *socket_path) {\n\tstruct sockaddr_un addr;\n\tint socketfd;\n\tif ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {\n\t\tsway_abort(\"Unable to open Unix socket\");\n\t}\n\taddr.sun_family = AF_UNIX;\n\tstrncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);\n\taddr.sun_path[sizeof(addr.sun_path) - 1] = 0;\n\tint l = sizeof(struct sockaddr_un);\n\tif (connect(socketfd, (struct sockaddr *)&addr, l) == -1) {\n\t\tsway_abort(\"Unable to connect to %s\", socket_path);\n\t}\n\treturn socketfd;\n}\n\nbool ipc_set_recv_timeout(int socketfd, struct timeval tv) {\n\tif (setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {\n\t\tsway_log_errno(SWAY_ERROR, \"Failed to set ipc recv timeout\");\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstruct ipc_response *ipc_recv_response(int socketfd) {\n\tchar data[IPC_HEADER_SIZE];\n\n\tsize_t total = 0;\n\twhile (total < IPC_HEADER_SIZE) {\n\t\tssize_t received = recv(socketfd, data + total, IPC_HEADER_SIZE - total, 0);\n\t\tif (received <= 0) {\n\t\t\tsway_abort(\"Unable to receive IPC response\");\n\t\t}\n\t\ttotal += received;\n\t}\n\n\tstruct ipc_response *response = malloc(sizeof(struct ipc_response));\n\tif (!response) {\n\t\tgoto error_1;\n\t}\n\n\tmemcpy(&response->size, data + sizeof(ipc_magic), sizeof(uint32_t));\n\tmemcpy(&response->type, data + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t));\n\n\tchar *payload = malloc(response->size + 1);\n\tif (!payload) {\n\t\tgoto error_2;\n\t}\n\n\ttotal = 0;\n\twhile (total < response->size) {\n\t\tssize_t received = recv(socketfd, payload + total, response->size - total, 0);\n\t\tif (received < 0) {\n\t\t\tsway_abort(\"Unable to receive IPC response\");\n\t\t}\n\t\ttotal += received;\n\t}\n\tpayload[response->size] = '\\0';\n\tresponse->payload = payload;\n\n\treturn response;\nerror_2:\n\tfree(response);\nerror_1:\n\tsway_log(SWAY_ERROR, \"Unable to allocate memory for IPC response\");\n\treturn NULL;\n}\n\nvoid free_ipc_response(struct ipc_response *response) {\n\tfree(response->payload);\n\tfree(response);\n}\n\nchar *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) {\n\tchar data[IPC_HEADER_SIZE];\n\tmemcpy(data, ipc_magic, sizeof(ipc_magic));\n\tmemcpy(data + sizeof(ipc_magic), len, sizeof(*len));\n\tmemcpy(data + sizeof(ipc_magic) + sizeof(*len), &type, sizeof(type));\n\n\tif (write(socketfd, data, IPC_HEADER_SIZE) == -1) {\n\t\tsway_abort(\"Unable to send IPC header\");\n\t}\n\n\tif (write(socketfd, payload, *len) == -1) {\n\t\tsway_abort(\"Unable to send IPC payload\");\n\t}\n\n\tstruct ipc_response *resp = ipc_recv_response(socketfd);\n\tchar *response = resp->payload;\n\t*len = resp->size;\n\tfree(resp);\n\n\treturn response;\n}\n"
  },
  {
    "path": "common/list.c",
    "content": "#include \"list.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"log.h\"\n\nlist_t *create_list(void) {\n\tlist_t *list = malloc(sizeof(list_t));\n\tif (!list) {\n\t\treturn NULL;\n\t}\n\tlist->capacity = 10;\n\tlist->length = 0;\n\tlist->items = malloc(sizeof(void*) * list->capacity);\n\treturn list;\n}\n\nstatic void list_resize(list_t *list) {\n\tif (list->length == list->capacity) {\n\t\tlist->capacity *= 2;\n\t\tlist->items = realloc(list->items, sizeof(void*) * list->capacity);\n\t}\n}\n\nvoid list_free(list_t *list) {\n\tif (list == NULL) {\n\t\treturn;\n\t}\n\tfree(list->items);\n\tfree(list);\n}\n\nvoid list_add(list_t *list, void *item) {\n\tlist_resize(list);\n\tlist->items[list->length++] = item;\n}\n\nvoid list_insert(list_t *list, int index, void *item) {\n\tlist_resize(list);\n\tmemmove(&list->items[index + 1], &list->items[index], sizeof(void*) * (list->length - index));\n\tlist->length++;\n\tlist->items[index] = item;\n}\n\nvoid list_del(list_t *list, int index) {\n\tlist->length--;\n\tmemmove(&list->items[index], &list->items[index + 1], sizeof(void*) * (list->length - index));\n}\n\nvoid list_cat(list_t *list, list_t *source) {\n\tfor (int i = 0; i < source->length; ++i) {\n\t\tlist_add(list, source->items[i]);\n\t}\n}\n\nvoid list_qsort(list_t *list, int compare(const void *left, const void *right)) {\n\tqsort(list->items, list->length, sizeof(void *), compare);\n}\n\nint list_seq_find(list_t *list, int compare(const void *item, const void *data), const void *data) {\n\tfor (int i = 0; i < list->length; i++) {\n\t\tvoid *item = list->items[i];\n\t\tif (compare(item, data) == 0) {\n\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\n\nint list_find(list_t *list, const void *item) {\n\tfor (int i = 0; i < list->length; i++) {\n\t\tif (list->items[i] == item) {\n\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\n\nvoid list_swap(list_t *list, int src, int dest) {\n\tvoid *tmp = list->items[src];\n\tlist->items[src] = list->items[dest];\n\tlist->items[dest] = tmp;\n}\n\nvoid list_move_to_end(list_t *list, void *item) {\n\tint i;\n\tfor (i = 0; i < list->length; ++i) {\n\t\tif (list->items[i] == item) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!sway_assert(i < list->length, \"Item not found in list\")) {\n\t\treturn;\n\t}\n\tlist_del(list, i);\n\tlist_add(list, item);\n}\n\nstatic void list_rotate(list_t *list, int from, int to) {\n\tvoid *tmp = list->items[to];\n\n\twhile (to > from) {\n\t\tlist->items[to] = list->items[to - 1];\n\t\tto--;\n\t}\n\n\tlist->items[from] = tmp;\n}\n\nstatic void list_inplace_merge(list_t *list, int left, int last, int mid, int compare(const void *a, const void *b)) {\n\tint right = mid + 1;\n\n\tif (compare(&list->items[mid], &list->items[right]) <= 0) {\n\t\treturn;\n\t}\n\n\twhile (left <= mid && right <= last) {\n\t\tif (compare(&list->items[left], &list->items[right]) <= 0) {\n\t\t\tleft++;\n\t\t} else {\n\t\t\tlist_rotate(list, left, right);\n\t\t\tleft++;\n\t\t\tmid++;\n\t\t\tright++;\n\t\t}\n\t}\n}\n\nstatic void list_inplace_sort(list_t *list, int first, int last, int compare(const void *a, const void *b)) {\n\tif (first >= last) {\n\t\treturn;\n\t} else if ((last - first) == 1) {\n\t\tif (compare(&list->items[first], &list->items[last]) > 0) {\n\t\t\tlist_swap(list, first, last);\n\t\t}\n\t} else {\n\t\tint mid = (int)((last + first) / 2);\n\t\tlist_inplace_sort(list, first, mid, compare);\n\t\tlist_inplace_sort(list, mid + 1, last, compare);\n\t\tlist_inplace_merge(list, first, last, mid, compare);\n\t}\n}\n\nvoid list_stable_sort(list_t *list, int compare(const void *a, const void *b)) {\n\tif (list->length > 1) {\n\t\tlist_inplace_sort(list, 0, list->length - 1, compare);\n\t}\n}\n\nvoid list_free_items_and_destroy(list_t *list) {\n\tif (!list) {\n\t\treturn;\n\t}\n\n\tfor (int i = 0; i < list->length; ++i) {\n\t\tfree(list->items[i]);\n\t}\n\tlist_free(list);\n}\n\n"
  },
  {
    "path": "common/log.c",
    "content": "#include <signal.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <unistd.h>\n#include \"log.h\"\n\nstatic terminate_callback_t log_terminate = exit;\n\nvoid _sway_abort(const char *format, ...) {\n\tva_list args;\n\tva_start(args, format);\n\t_sway_vlog(SWAY_ERROR, format, args);\n\tva_end(args);\n\tlog_terminate(EXIT_FAILURE);\n}\n\nbool _sway_assert(bool condition, const char *format, ...) {\n\tif (condition) {\n\t\treturn true;\n\t}\n\n\tva_list args;\n\tva_start(args, format);\n\t_sway_vlog(SWAY_ERROR, format, args);\n\tva_end(args);\n\n#ifndef NDEBUG\n\traise(SIGABRT);\n#endif\n\n\treturn false;\n}\n\nstatic bool colored = true;\nstatic sway_log_importance_t log_importance = SWAY_ERROR;\nstatic struct timespec start_time = {-1, -1};\n\nstatic const char *verbosity_colors[] = {\n\t[SWAY_SILENT] = \"\",\n\t[SWAY_ERROR ] = \"\\x1B[1;31m\",\n\t[SWAY_INFO  ] = \"\\x1B[1;34m\",\n\t[SWAY_DEBUG ] = \"\\x1B[1;90m\",\n};\n\nstatic const char *verbosity_headers[] = {\n\t[SWAY_SILENT] = \"\",\n\t[SWAY_ERROR] = \"[ERROR]\",\n\t[SWAY_INFO] = \"[INFO]\",\n\t[SWAY_DEBUG] = \"[DEBUG]\",\n};\n\nstatic void timespec_sub(struct timespec *r, const struct timespec *a,\n\t\tconst struct timespec *b) {\n\tconst long NSEC_PER_SEC = 1000000000;\n\tr->tv_sec = a->tv_sec - b->tv_sec;\n\tr->tv_nsec = a->tv_nsec - b->tv_nsec;\n\tif (r->tv_nsec < 0) {\n\t\tr->tv_sec--;\n\t\tr->tv_nsec += NSEC_PER_SEC;\n\t}\n}\n\nstatic void init_start_time(void) {\n\tif (start_time.tv_sec >= 0) {\n\t\treturn;\n\t}\n\tclock_gettime(CLOCK_MONOTONIC, &start_time);\n}\n\nstatic void sway_log_stderr(sway_log_importance_t verbosity, const char *fmt,\n\t\tva_list args) {\n\tinit_start_time();\n\n\tif (verbosity > log_importance) {\n\t\treturn;\n\t}\n\n\tstruct timespec ts = {0};\n\tclock_gettime(CLOCK_MONOTONIC, &ts);\n\ttimespec_sub(&ts, &ts, &start_time);\n\n\tfprintf(stderr, \"%02d:%02d:%02d.%03ld \", (int)(ts.tv_sec / 60 / 60),\n\t\t(int)(ts.tv_sec / 60 % 60), (int)(ts.tv_sec % 60),\n\t\tts.tv_nsec / 1000000);\n\n\tunsigned c = (verbosity < SWAY_LOG_IMPORTANCE_LAST) ? verbosity :\n\t\tSWAY_LOG_IMPORTANCE_LAST - 1;\n\n\tif (colored && isatty(STDERR_FILENO)) {\n\t\tfprintf(stderr, \"%s\", verbosity_colors[c]);\n\t} else {\n\t\tfprintf(stderr, \"%s \", verbosity_headers[c]);\n\t}\n\n\tvfprintf(stderr, fmt, args);\n\n\tif (colored && isatty(STDERR_FILENO)) {\n\t\tfprintf(stderr, \"\\x1B[0m\");\n\t}\n\tfprintf(stderr, \"\\n\");\n}\n\nvoid sway_log_init(sway_log_importance_t verbosity, terminate_callback_t callback) {\n\tinit_start_time();\n\n\tif (verbosity < SWAY_LOG_IMPORTANCE_LAST) {\n\t\tlog_importance = verbosity;\n\t}\n\tif (callback) {\n\t\tlog_terminate = callback;\n\t}\n}\n\nvoid _sway_vlog(sway_log_importance_t verbosity, const char *fmt, va_list args) {\n\tsway_log_stderr(verbosity, fmt, args);\n}\n\nvoid _sway_log(sway_log_importance_t verbosity, const char *fmt, ...) {\n\tva_list args;\n\tva_start(args, fmt);\n\tsway_log_stderr(verbosity, fmt, args);\n\tva_end(args);\n}\n"
  },
  {
    "path": "common/loop.c",
    "content": "#include <limits.h>\n#include <string.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <poll.h>\n#include <time.h>\n#include <unistd.h>\n#include \"list.h\"\n#include \"log.h\"\n#include \"loop.h\"\n\nstruct loop_fd_event {\n\tvoid (*callback)(int fd, short mask, void *data);\n\tvoid *data;\n};\n\nstruct loop_timer {\n\tvoid (*callback)(void *data);\n\tvoid *data;\n\tstruct timespec expiry;\n};\n\nstruct loop {\n\tstruct pollfd *fds;\n\tint fd_length;\n\tint fd_capacity;\n\n\tlist_t *fd_events; // struct loop_fd_event\n\tlist_t *timers; // struct loop_timer\n};\n\nstruct loop *loop_create(void) {\n\tstruct loop *loop = calloc(1, sizeof(struct loop));\n\tif (!loop) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate memory for loop\");\n\t\treturn NULL;\n\t}\n\tloop->fd_capacity = 10;\n\tloop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity);\n\tloop->fd_events = create_list();\n\tloop->timers = create_list();\n\treturn loop;\n}\n\nvoid loop_destroy(struct loop *loop) {\n\tlist_free_items_and_destroy(loop->fd_events);\n\tlist_free_items_and_destroy(loop->timers);\n\tfree(loop->fds);\n\tfree(loop);\n}\n\nvoid loop_poll(struct loop *loop) {\n\t// Calculate next timer in ms\n\tint ms = INT_MAX;\n\tif (loop->timers->length) {\n\t\tstruct timespec now;\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\t\tfor (int i = 0; i < loop->timers->length; ++i) {\n\t\t\tstruct loop_timer *timer = loop->timers->items[i];\n\t\t\tint timer_ms = (timer->expiry.tv_sec - now.tv_sec) * 1000;\n\t\t\ttimer_ms += (timer->expiry.tv_nsec - now.tv_nsec) / 1000000;\n\t\t\tif (timer_ms < ms) {\n\t\t\t\tms = timer_ms;\n\t\t\t}\n\t\t}\n\t}\n\tif (ms < 0) {\n\t\tms = 0;\n\t}\n\n\tpoll(loop->fds, loop->fd_length, ms);\n\n\t// Dispatch fds\n\tfor (int i = 0; i < loop->fd_length; ++i) {\n\t\tstruct pollfd pfd = loop->fds[i];\n\t\tstruct loop_fd_event *event = loop->fd_events->items[i];\n\n\t\t// Always send these events\n\t\tunsigned events = pfd.events | POLLHUP | POLLERR;\n\n\t\tif (pfd.revents & events) {\n\t\t\tevent->callback(pfd.fd, pfd.revents, event->data);\n\t\t}\n\t}\n\n\t// Dispatch timers\n\tif (loop->timers->length) {\n\t\tstruct timespec now;\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\t\tfor (int i = 0; i < loop->timers->length; ++i) {\n\t\t\tstruct loop_timer *timer = loop->timers->items[i];\n\t\t\tbool expired = timer->expiry.tv_sec < now.tv_sec ||\n\t\t\t\t(timer->expiry.tv_sec == now.tv_sec &&\n\t\t\t\t timer->expiry.tv_nsec < now.tv_nsec);\n\t\t\tif (expired) {\n\t\t\t\ttimer->callback(timer->data);\n\t\t\t\tloop_remove_timer(loop, timer);\n\t\t\t\t--i;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid loop_add_fd(struct loop *loop, int fd, short mask,\n\t\tvoid (*callback)(int fd, short mask, void *data), void *data) {\n\tstruct loop_fd_event *event = calloc(1, sizeof(struct loop_fd_event));\n\tif (!event) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate memory for event\");\n\t\treturn;\n\t}\n\tevent->callback = callback;\n\tevent->data = data;\n\tlist_add(loop->fd_events, event);\n\n\tstruct pollfd pfd = {fd, mask, 0};\n\n\tif (loop->fd_length == loop->fd_capacity) {\n\t\tint capacity = loop->fd_capacity + 10;\n\t\tstruct pollfd *tmp = realloc(loop->fds,\n\t\t\t\tsizeof(struct pollfd) * capacity);\n\t\tif (!tmp) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to allocate memory for pollfd\");\n\t\t\treturn;\n\t\t}\n\t\tloop->fds = tmp;\n\t\tloop->fd_capacity = capacity;\n\t}\n\n\tloop->fds[loop->fd_length++] = pfd;\n}\n\nstruct loop_timer *loop_add_timer(struct loop *loop, int ms,\n\t\tvoid (*callback)(void *data), void *data) {\n\tstruct loop_timer *timer = calloc(1, sizeof(struct loop_timer));\n\tif (!timer) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate memory for timer\");\n\t\treturn NULL;\n\t}\n\ttimer->callback = callback;\n\ttimer->data = data;\n\n\tclock_gettime(CLOCK_MONOTONIC, &timer->expiry);\n\ttimer->expiry.tv_sec += ms / 1000;\n\n\tlong int nsec = (ms % 1000) * 1000000;\n\tif (timer->expiry.tv_nsec + nsec >= 1000000000) {\n\t\ttimer->expiry.tv_sec++;\n\t\tnsec -= 1000000000;\n\t}\n\ttimer->expiry.tv_nsec += nsec;\n\n\tlist_add(loop->timers, timer);\n\n\treturn timer;\n}\n\nbool loop_remove_fd(struct loop *loop, int fd) {\n\tfor (int i = 0; i < loop->fd_length; ++i) {\n\t\tif (loop->fds[i].fd == fd) {\n\t\t\tfree(loop->fd_events->items[i]);\n\t\t\tlist_del(loop->fd_events, i);\n\n\t\t\tloop->fd_length--;\n\t\t\tmemmove(&loop->fds[i], &loop->fds[i + 1],\n\t\t\t\t\tsizeof(struct pollfd) * (loop->fd_length - i));\n\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool loop_remove_timer(struct loop *loop, struct loop_timer *timer) {\n\tfor (int i = 0; i < loop->timers->length; ++i) {\n\t\tif (loop->timers->items[i] == timer) {\n\t\t\tlist_del(loop->timers, i);\n\t\t\tfree(timer);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "common/meson.build",
    "content": "lib_sway_common = static_library(\n\t'sway-common',\n\tfiles(\n\t\t'cairo.c',\n\t\t'gesture.c',\n\t\t'ipc-client.c',\n\t\t'log.c',\n\t\t'loop.c',\n\t\t'list.c',\n\t\t'pango.c',\n\t\t'stringop.c',\n\t\t'util.c'\n\t),\n\tdependencies: [\n\t\tcairo,\n\t\tpango,\n\t\tpangocairo,\n\t\twayland_client.partial_dependency(compile_args: true)\n\t],\n\tinclude_directories: sway_inc\n)\n"
  },
  {
    "path": "common/pango.c",
    "content": "#include <cairo.h>\n#include <pango/pangocairo.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"cairo_util.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nsize_t escape_markup_text(const char *src, char *dest) {\n\tsize_t length = 0;\n\tif (dest) {\n\t\tdest[0] = '\\0';\n\t}\n\n\twhile (src[0]) {\n\t\tswitch (src[0]) {\n\t\tcase '&':\n\t\t\tlength += 5;\n\t\t\tlenient_strcat(dest, \"&amp;\");\n\t\t\tbreak;\n\t\tcase '<':\n\t\t\tlength += 4;\n\t\t\tlenient_strcat(dest, \"&lt;\");\n\t\t\tbreak;\n\t\tcase '>':\n\t\t\tlength += 4;\n\t\t\tlenient_strcat(dest, \"&gt;\");\n\t\t\tbreak;\n\t\tcase '\\'':\n\t\t\tlength += 6;\n\t\t\tlenient_strcat(dest, \"&apos;\");\n\t\t\tbreak;\n\t\tcase '\"':\n\t\t\tlength += 6;\n\t\t\tlenient_strcat(dest, \"&quot;\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (dest) {\n\t\t\t\tdest[length] = *src;\n\t\t\t\tdest[length + 1] = '\\0';\n\t\t\t}\n\t\t\tlength += 1;\n\t\t}\n\t\tsrc++;\n\t}\n\treturn length;\n}\n\nPangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,\n\t\tconst char *text, double scale, bool markup) {\n\tPangoLayout *layout = pango_cairo_create_layout(cairo);\n\tpango_context_set_round_glyph_positions(pango_layout_get_context(layout), false);\n\n\tPangoAttrList *attrs;\n\tif (markup) {\n\t\tchar *buf;\n\t\tGError *error = NULL;\n\t\tif (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) {\n\t\t\tpango_layout_set_text(layout, buf, -1);\n\t\t\tfree(buf);\n\t\t} else {\n\t\t\tsway_log(SWAY_ERROR, \"pango_parse_markup '%s' -> error %s\", text,\n\t\t\t\t\terror->message);\n\t\t\tg_error_free(error);\n\t\t\tmarkup = false; // fallback to plain text\n\t\t}\n\t}\n\tif (!markup) {\n\t\tattrs = pango_attr_list_new();\n\t\tpango_layout_set_text(layout, text, -1);\n\t}\n\n\tpango_attr_list_insert(attrs, pango_attr_scale_new(scale));\n\tpango_layout_set_font_description(layout, desc);\n\tpango_layout_set_single_paragraph_mode(layout, 1);\n\tpango_layout_set_attributes(layout, attrs);\n\tpango_attr_list_unref(attrs);\n\treturn layout;\n}\n\nvoid get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,\n\t\tint *baseline, double scale, bool markup, const char *fmt, ...) {\n\tif (width) {\n\t\t*width = 0;\n\t}\n\tif (height) {\n\t\t*height = 0;\n\t}\n\tif (baseline) {\n\t\t*baseline = 0;\n\t}\n\n\tva_list args;\n\tva_start(args, fmt);\n\tchar *buf = vformat_str(fmt, args);\n\tva_end(args);\n\tif (buf == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Failed to format string\");\n\t\treturn;\n\t}\n\n\tPangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);\n\tpango_cairo_update_layout(cairo, layout);\n\tcairo_status_t status = cairo_status(cairo);\n\tif (status != CAIRO_STATUS_SUCCESS) {\n\t\tsway_log(SWAY_ERROR, \"pango_cairo_update_layout() failed: %s\",\n\t\t\tcairo_status_to_string(status));\n\t\tgoto out;\n\t}\n\n\tpango_layout_get_pixel_size(layout, width, height);\n\tif (baseline) {\n\t\t*baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;\n\t}\n\nout:\n\tg_object_unref(layout);\n\tfree(buf);\n}\n\nvoid get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) {\n\tcairo_t *cairo = cairo_create(NULL);\n\tPangoContext *pango = pango_cairo_create_context(cairo);\n\tpango_context_set_round_glyph_positions(pango, false);\n\t// When passing NULL as a language, pango uses the current locale.\n\tPangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);\n\n\t*baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;\n\t*height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;\n\n\tpango_font_metrics_unref(metrics);\n\tg_object_unref(pango);\n\tcairo_destroy(cairo);\n}\n\nvoid render_text(cairo_t *cairo, const PangoFontDescription *desc,\n\t\tdouble scale, bool markup, const char *fmt, ...) {\n\tva_list args;\n\tva_start(args, fmt);\n\tchar *buf = vformat_str(fmt, args);\n\tva_end(args);\n\tif (buf == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Failed to format string\");\n\t\treturn;\n\t}\n\n\tPangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);\n\tcairo_font_options_t *fo = cairo_font_options_create();\n\tcairo_get_font_options(cairo, fo);\n\tpango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);\n\tcairo_font_options_destroy(fo);\n\n\tpango_cairo_update_layout(cairo, layout);\n\tcairo_status_t status = cairo_status(cairo);\n\tif (status != CAIRO_STATUS_SUCCESS) {\n\t\tsway_log(SWAY_ERROR, \"pango_cairo_update_layout() failed: %s\",\n\t\t\tcairo_status_to_string(status));\n\t\tgoto out;\n\t}\n\n\tpango_cairo_show_layout(cairo, layout);\n\nout:\n\tg_object_unref(layout);\n\tfree(buf);\n}\n"
  },
  {
    "path": "common/stringop.c",
    "content": "#include <ctype.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <wordexp.h>\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic const char whitespace[] = \" \\f\\n\\r\\t\\v\";\n\nvoid strip_whitespace(char *str) {\n\tsize_t len = strlen(str);\n\tsize_t start = strspn(str, whitespace);\n\tmemmove(str, &str[start], len + 1 - start);\n\n\tif (*str) {\n\t\tfor (len -= start + 1; isspace(str[len]); --len) {}\n\t\tstr[len + 1] = '\\0';\n\t}\n}\n\nvoid strip_quotes(char *str) {\n\tbool in_str = false;\n\tbool in_chr = false;\n\tbool escaped = false;\n\tchar *end = strchr(str,0);\n\twhile (*str) {\n\t\tif (*str == '\\'' && !in_str && !escaped) {\n\t\t\tin_chr = !in_chr;\n\t\t\tgoto shift_over;\n\t\t} else if (*str == '\\\"' && !in_chr && !escaped) {\n\t\t\tin_str = !in_str;\n\t\t\tgoto shift_over;\n\t\t} else if (*str == '\\\\') {\n\t\t\tescaped = !escaped;\n\t\t\t++str;\n\t\t\tcontinue;\n\t\t}\n\t\tescaped = false;\n\t\t++str;\n\t\tcontinue;\n\t\tshift_over:\n\t\tmemmove(str, str+1, end-- - str);\n\t}\n\t*end = '\\0';\n}\n\nchar *lenient_strcat(char *dest, const char *src) {\n\tif (dest && src) {\n\t\treturn strcat(dest, src);\n\t}\n\treturn dest;\n}\n\nchar *lenient_strncat(char *dest, const char *src, size_t len) {\n\tif (dest && src) {\n\t\treturn strncat(dest, src, len);\n\t}\n\treturn dest;\n}\n\n// strcmp that also handles null pointers.\nint lenient_strcmp(const char *a, const char *b) {\n\tif (a == b) {\n\t\treturn 0;\n\t} else if (!a) {\n\t\treturn -1;\n\t} else if (!b) {\n\t\treturn 1;\n\t} else {\n\t\treturn strcmp(a, b);\n\t}\n}\n\nlist_t *split_string(const char *str, const char *delims) {\n\tlist_t *res = create_list();\n\tchar *copy = strdup(str);\n\n\tchar *token = strtok(copy, delims);\n\twhile (token) {\n\t\tlist_add(res, strdup(token));\n\t\ttoken = strtok(NULL, delims);\n\t}\n\tfree(copy);\n\treturn res;\n}\n\nchar **split_args(const char *start, int *argc) {\n\t*argc = 0;\n\tint alloc = 2;\n\tchar **argv = malloc(sizeof(char *) * alloc);\n\tbool in_token = false;\n\tbool in_string = false;\n\tbool in_char = false;\n\tbool in_brackets = false; // brackets are used for criteria\n\tbool escaped = false;\n\tconst char *end = start;\n\tif (start) {\n\t\twhile (*start) {\n\t\t\tif (!in_token) {\n\t\t\t\tstart = (end += strspn(end, whitespace));\n\t\t\t\tin_token = true;\n\t\t\t}\n\t\t\tif (*end == '\"' && !in_char && !escaped) {\n\t\t\t\tin_string = !in_string;\n\t\t\t} else if (*end == '\\'' && !in_string && !escaped) {\n\t\t\t\tin_char = !in_char;\n\t\t\t} else if (*end == '[' && !in_string && !in_char && !in_brackets && !escaped) {\n\t\t\t\tin_brackets = true;\n\t\t\t} else if (*end == ']' && !in_string && !in_char && in_brackets && !escaped) {\n\t\t\t\tin_brackets = false;\n\t\t\t} else if (*end == '\\\\') {\n\t\t\t\tescaped = !escaped;\n\t\t\t} else if (*end == '\\0' || (!in_string && !in_char && !in_brackets\n\t\t\t\t\t\t&& !escaped && strchr(whitespace, *end))) {\n\t\t\t\tgoto add_token;\n\t\t\t}\n\t\t\tif (*end != '\\\\') {\n\t\t\t\tescaped = false;\n\t\t\t}\n\t\t\t++end;\n\t\t\tcontinue;\n\t\t\tadd_token:\n\t\t\tif (end - start > 0) {\n\t\t\t\tchar *token = malloc(end - start + 1);\n\t\t\t\tstrncpy(token, start, end - start + 1);\n\t\t\t\ttoken[end - start] = '\\0';\n\t\t\t\targv[*argc] = token;\n\t\t\t\tif (++*argc + 1 == alloc) {\n\t\t\t\t\targv = realloc(argv, (alloc *= 2) * sizeof(char *));\n\t\t\t\t}\n\t\t\t}\n\t\t\tin_token = false;\n\t\t\tescaped = false;\n\t\t}\n\t}\n\targv[*argc] = NULL;\n\treturn argv;\n}\n\nvoid free_argv(int argc, char **argv) {\n\twhile (argc-- > 0) {\n\t\tfree(argv[argc]);\n\t}\n\tfree(argv);\n}\n\nint unescape_string(char *string) {\n\t/* TODO: More C string escapes */\n\tint len = strlen(string);\n\tint i;\n\tfor (i = 0; string[i]; ++i) {\n\t\tif (string[i] == '\\\\') {\n\t\t\tswitch (string[++i]) {\n\t\t\tcase '0':\n\t\t\t\tstring[i - 1] = '\\0';\n\t\t\t\treturn i - 1;\n\t\t\tcase 'a':\n\t\t\t\tstring[i - 1] = '\\a';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase 'b':\n\t\t\t\tstring[i - 1] = '\\b';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase 'f':\n\t\t\t\tstring[i - 1] = '\\f';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase 'n':\n\t\t\t\tstring[i - 1] = '\\n';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase 'r':\n\t\t\t\tstring[i - 1] = '\\r';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase 't':\n\t\t\t\tstring[i - 1] = '\\t';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase 'v':\n\t\t\t\tstring[i - 1] = '\\v';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase '\\\\':\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase '\\'':\n\t\t\t\tstring[i - 1] = '\\'';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase '\\\"':\n\t\t\t\tstring[i - 1] = '\\\"';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase '?':\n\t\t\t\tstring[i - 1] = '?';\n\t\t\t\tstring[i] = '\\0';\n\t\t\t\tbreak;\n\t\t\tcase 'x':\n\t\t\t\t{\n\t\t\t\t\tunsigned char c = 0;\n\t\t\t\t\tif (string[i+1] >= '0' && string[i+1] <= '9') {\n\t\t\t\t\t\tc = string[i+1] - '0';\n\t\t\t\t\t\tif (string[i+2] >= '0' && string[i+2] <= '9') {\n\t\t\t\t\t\t\tc *= 0x10;\n\t\t\t\t\t\t\tc += string[i+2] - '0';\n\t\t\t\t\t\t\tstring[i+2] = '\\0';\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstring[i+1] = '\\0';\n\t\t\t\t\t}\n\t\t\t\t\tstring[i] = '\\0';\n\t\t\t\t\tstring[i - 1] = c;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Shift characters over nullspaces\n\tint shift = 0;\n\tfor (i = 0; i < len; ++i) {\n\t\tif (string[i] == 0) {\n\t\t\tshift++;\n\t\t\tcontinue;\n\t\t}\n\t\tstring[i-shift] = string[i];\n\t}\n\tstring[len - shift] = 0;\n\treturn len - shift;\n}\n\nchar *join_args(char **argv, int argc) {\n\tif (!sway_assert(argc > 0, \"argc should be positive\")) {\n\t\treturn NULL;\n\t}\n\tint len = 0, i;\n\tfor (i = 0; i < argc; ++i) {\n\t\tlen += strlen(argv[i]) + 1;\n\t}\n\tchar *res = malloc(len);\n\tlen = 0;\n\tfor (i = 0; i < argc; ++i) {\n\t\tstrcpy(res + len, argv[i]);\n\t\tlen += strlen(argv[i]);\n\t\tres[len++] = ' ';\n\t}\n\tres[len - 1] = '\\0';\n\treturn res;\n}\n\nstatic inline char *argsep_next_interesting(const char *src, const char *delim) {\n\tchar *special = strpbrk(src, \"\\\"'\\\\\");\n\tchar *next_delim = strpbrk(src, delim);\n\tif (!special) {\n\t\treturn next_delim;\n\t}\n\tif (!next_delim) {\n\t\treturn special;\n\t}\n\treturn (next_delim < special) ? next_delim : special;\n}\n\nchar *argsep(char **stringp, const char *delim, char *matched) {\n\tchar *start = *stringp;\n\tchar *end = start;\n\tbool in_string = false;\n\tbool in_char = false;\n\tbool escaped = false;\n\tchar *interesting = NULL;\n\n\twhile ((interesting = argsep_next_interesting(end, delim))) {\n\t\tif (escaped && interesting != end) {\n\t\t\tescaped = false;\n\t\t}\n\t\tif (*interesting == '\"' && !in_char && !escaped) {\n\t\t\tin_string = !in_string;\n\t\t\tend = interesting + 1;\n\t\t} else if (*interesting == '\\'' && !in_string && !escaped) {\n\t\t\tin_char = !in_char;\n\t\t\tend = interesting + 1;\n\t\t} else if (*interesting == '\\\\') {\n\t\t\tescaped = !escaped;\n\t\t\tend = interesting + 1;\n\t\t} else if (!in_string && !in_char && !escaped) {\n\t\t\t// We must have matched a separator\n\t\t\tend = interesting;\n\t\t\tif (matched) {\n\t\t\t\t*matched = *end;\n\t\t\t}\n\t\t\tif (end - start) {\n\t\t\t\t*(end++) = 0;\n\t\t\t\t*stringp = end;\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tend = ++start;\n\t\t\t}\n\t\t} else {\n\t\t\tend++;\n\t\t}\n\t}\n\tif (!interesting) {\n\t\t*stringp = NULL;\n\t\tif (matched) {\n\t\t\t*matched = '\\0';\n\t\t}\n\t}\n\treturn start;\n}\n\nbool expand_path(char **path) {\n\twordexp_t p = {0};\n\twhile (strstr(*path, \"  \")) {\n\t\t*path = realloc(*path, strlen(*path) + 2);\n\t\tchar *ptr = strstr(*path, \"  \") + 1;\n\t\tmemmove(ptr + 1, ptr, strlen(ptr) + 1);\n\t\t*ptr = '\\\\';\n\t}\n\tif (wordexp(*path, &p, 0) != 0 || p.we_wordv[0] == NULL) {\n\t\twordfree(&p);\n\t\treturn false;\n\t}\n\tfree(*path);\n\t*path = join_args(p.we_wordv, p.we_wordc);\n\twordfree(&p);\n\treturn true;\n}\n\nchar *vformat_str(const char *fmt, va_list args) {\n\tchar *str = NULL;\n\tva_list args_copy;\n\tva_copy(args_copy, args);\n\n\tint len = vsnprintf(NULL, 0, fmt, args);\n\tif (len < 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"vsnprintf(\\\"%s\\\") failed\", fmt);\n\t\tgoto out;\n\t}\n\n\tstr = malloc(len + 1);\n\tif (str == NULL) {\n\t\tsway_log_errno(SWAY_ERROR, \"malloc() failed\");\n\t\tgoto out;\n\t}\n\n\tvsnprintf(str, len + 1, fmt, args_copy);\n\nout:\n\tva_end(args_copy);\n\treturn str;\n}\n\nchar *format_str(const char *fmt, ...) {\n\tva_list args;\n\tva_start(args, fmt);\n\tchar *str = vformat_str(fmt, args);\n\tva_end(args);\n\treturn str;\n}\n\nbool has_prefix(const char *str, const char *prefix) {\n\treturn strncmp(str, prefix, strlen(prefix)) == 0;\n}\n"
  },
  {
    "path": "common/util.c",
    "content": "#include <ctype.h>\n#include <fcntl.h>\n#include <math.h>\n#include <time.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <wayland-server-protocol.h>\n#include \"log.h\"\n#include \"util.h\"\n\nint wrap(int i, int max) {\n\treturn ((i % max) + max) % max;\n}\n\nbool parse_color(const char *color, uint32_t *result) {\n\tif (color[0] == '#') {\n\t\t++color;\n\t}\n\tint len = strlen(color);\n\tif ((len != 6 && len != 8) || !isxdigit(color[0]) || !isxdigit(color[1])) {\n\t\treturn false;\n\t}\n\tchar *ptr;\n\tuint32_t parsed = strtoul(color, &ptr, 16);\n\tif (*ptr != '\\0') {\n\t\treturn false;\n\t}\n\t*result = len == 6 ? ((parsed << 8) | 0xFF) : parsed;\n\treturn true;\n}\n\nvoid color_to_rgba(float dest[static 4], uint32_t color) {\n\tdest[0] = ((color >> 24) & 0xff) / 255.0;\n\tdest[1] = ((color >> 16) & 0xff) / 255.0;\n\tdest[2] = ((color >> 8) & 0xff) / 255.0;\n\tdest[3] = (color & 0xff) / 255.0;\n}\n\nbool parse_boolean(const char *boolean, bool current) {\n\tif (strcasecmp(boolean, \"1\") == 0\n\t\t\t|| strcasecmp(boolean, \"yes\") == 0\n\t\t\t|| strcasecmp(boolean, \"on\") == 0\n\t\t\t|| strcasecmp(boolean, \"true\") == 0\n\t\t\t|| strcasecmp(boolean, \"enable\") == 0\n\t\t\t|| strcasecmp(boolean, \"enabled\") == 0\n\t\t\t|| strcasecmp(boolean, \"active\") == 0) {\n\t\treturn true;\n\t} else if (strcasecmp(boolean, \"toggle\") == 0) {\n\t\treturn !current;\n\t}\n\t// All other values are false to match i3\n\treturn false;\n}\n\nfloat parse_float(const char *value) {\n\terrno = 0;\n\tchar *end;\n\tfloat flt = strtof(value, &end);\n\tif (*end || errno) {\n\t\tsway_log(SWAY_DEBUG, \"Invalid float value '%s', defaulting to NAN\", value);\n\t\treturn NAN;\n\t}\n\treturn flt;\n}\n\nenum movement_unit parse_movement_unit(const char *unit) {\n\tif (strcasecmp(unit, \"px\") == 0) {\n\t\treturn MOVEMENT_UNIT_PX;\n\t}\n\tif (strcasecmp(unit, \"ppt\") == 0) {\n\t\treturn MOVEMENT_UNIT_PPT;\n\t}\n\tif (strcasecmp(unit, \"default\") == 0) {\n\t\treturn MOVEMENT_UNIT_DEFAULT;\n\t}\n\treturn MOVEMENT_UNIT_INVALID;\n}\n\nint parse_movement_amount(int argc, char **argv,\n\t\tstruct movement_amount *amount) {\n\tif (!sway_assert(argc > 0, \"Expected args in parse_movement_amount\")) {\n\t\tamount->amount = 0;\n\t\tamount->unit = MOVEMENT_UNIT_INVALID;\n\t\treturn 0;\n\t}\n\n\tchar *err;\n\tamount->amount = (int)strtol(argv[0], &err, 10);\n\tif (*err) {\n\t\t// e.g. 10px\n\t\tamount->unit = parse_movement_unit(err);\n\t\treturn 1;\n\t}\n\tif (argc == 1) {\n\t\tamount->unit = MOVEMENT_UNIT_DEFAULT;\n\t\treturn 1;\n\t}\n\t// Try the second argument\n\tamount->unit = parse_movement_unit(argv[1]);\n\tif (amount->unit == MOVEMENT_UNIT_INVALID) {\n\t\tamount->unit = MOVEMENT_UNIT_DEFAULT;\n\t\treturn 1;\n\t}\n\treturn 2;\n}\n\nconst char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) {\n\tswitch (subpixel) {\n\tcase WL_OUTPUT_SUBPIXEL_UNKNOWN:\n\t\treturn \"unknown\";\n\tcase WL_OUTPUT_SUBPIXEL_NONE:\n\t\treturn \"none\";\n\tcase WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:\n\t\treturn \"rgb\";\n\tcase WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:\n\t\treturn \"bgr\";\n\tcase WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:\n\t\treturn \"vrgb\";\n\tcase WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:\n\t\treturn \"vbgr\";\n\t}\n\tsway_assert(false, \"Unknown value for wl_output_subpixel.\");\n\treturn NULL;\n}\n\nbool sway_set_cloexec(int fd, bool cloexec) {\n\tint flags = fcntl(fd, F_GETFD);\n\tif (flags == -1) {\n\t\tsway_log_errno(SWAY_ERROR, \"fcntl failed\");\n\t\treturn false;\n\t}\n\tif (cloexec) {\n\t\tflags = flags | FD_CLOEXEC;\n\t} else {\n\t\tflags = flags & ~FD_CLOEXEC;\n\t}\n\tif (fcntl(fd, F_SETFD, flags) == -1) {\n\t\tsway_log_errno(SWAY_ERROR, \"fcntl failed\");\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nuint32_t get_current_time_in_msec(void) {\n\tstruct timespec now;\n\tclock_gettime(CLOCK_MONOTONIC, &now);\n\treturn now.tv_sec * 1000 + now.tv_nsec / 1000000;\n}\n"
  },
  {
    "path": "completions/bash/sway",
    "content": "# sway(1) completion\n\n_sway()\n{\n  local cur prev short long\n  _get_comp_words_by_ref cur prev\n\n  short=(\n    -h\n    -c\n    -C\n    -d\n    -v\n    -V\n  )\n\n  long=(\n    --help\n    --config\n    --validate\n    --debug\n    --version\n    --verbose\n    --get-socketpath\n  )\n\n  case $prev in\n    -c|--config)\n      _filedir\n      return\n      ;;\n  esac\n\n  if [[ $cur == --* ]]; then\n    COMPREPLY=($(compgen -W \"${long[*]}\" -- \"$cur\"))\n  elif [[ $cur == -* ]]; then\n    COMPREPLY=($(compgen -W \"${short[*]}\" -- \"$cur\"))\n    COMPREPLY+=($(compgen -W \"${long[*]}\" -- \"$cur\"))\n  else\n    COMPREPLY=($(compgen -W \"${short[*]}\" -- \"$cur\"))\n    COMPREPLY+=($(compgen -W \"${long[*]}\" -- \"$cur\"))\n    COMPREPLY+=($(compgen -c -- \"$cur\"))\n  fi\n\n} &&\ncomplete -F _sway sway\n"
  },
  {
    "path": "completions/bash/swaybar",
    "content": "# swaybar(1) completion\n\n_swaybar()\n{\n  local cur prev short long\n  _get_comp_words_by_ref cur prev\n\n  short=(\n    -h\n    -v\n    -s\n    -b\n    -d\n  )\n\n  long=(\n    --help\n    --version\n    --socket\n    --bar_id\n    --debug\n  )\n\n  case $prev in\n    -s|--socket)\n      _filedir\n      return\n      ;;\n    -b|--bar_id)\n      bars=($(swaymsg -t get_bar_config | jq -r '.[]'))\n      COMPREPLY=($(compgen -W \"${bars[*]}\" -- \"$cur\"))\n      return\n      ;;\n  esac\n\n  if [[ $cur == --* ]]; then\n    COMPREPLY=($(compgen -W \"${long[*]}\" -- \"$cur\"))\n  else\n    COMPREPLY=($(compgen -W \"${short[*]}\" -- \"$cur\"))\n    COMPREPLY+=($(compgen -W \"${long[*]}\" -- \"$cur\"))\n  fi\n\n} &&\ncomplete -F _swaybar swaybar\n"
  },
  {
    "path": "completions/bash/swaymsg",
    "content": "# swaymsg(1) completion\n\n_swaymsg()\n{\n  local cur prev types short long\n  _get_comp_words_by_ref cur prev\n\n  types=(\n    'get_workspaces'\n    'get_seats'\n    'get_inputs'\n    'get_outputs'\n    'get_tree'\n    'get_marks'\n    'get_bar_config'\n    'get_version'\n    'get_binding_modes'\n    'get_binding_state'\n    'get_config'\n    'send_tick'\n    'subscribe'\n  )\n\n  short=(\n    -h\n    -m\n    -p\n    -q\n    -r\n    -s\n    -t\n    -v\n  )\n\n  long=(\n    --help\n    --monitor\n    --pretty\n    --quiet\n    --raw\n    --socket\n    --type\n    --version\n  )\n\n  case $prev in\n    -s|--socket)\n      _filedir\n      return\n      ;;\n    -t|--type)\n      COMPREPLY=($(compgen -W \"${types[*]}\" -- \"$cur\"))\n      return\n      ;;\n  esac\n\n  if [[ $cur == --* ]]; then\n    COMPREPLY=($(compgen -W \"${long[*]}\" -- \"$cur\"))\n  else\n    COMPREPLY=($(compgen -W \"${short[*]}\" -- \"$cur\"))\n    COMPREPLY+=($(compgen -W \"${long[*]}\" -- \"$cur\"))\n  fi\n\n} &&\ncomplete -F _swaymsg swaymsg\n"
  },
  {
    "path": "completions/fish/sway.fish",
    "content": "# sway(1) completion\n\ncomplete -f -c sway\ncomplete -c sway -s h -l help --description \"Show help message and quit.\"\ncomplete -c sway -s c -l config --description \"Specifies a config file.\" -r\ncomplete -c sway -s C -l validate --description \"Check the validity of the config file, then exit.\"\ncomplete -c sway -s d -l debug --description \"Enables full logging, including debug information.\"\ncomplete -c sway -s v -l version --description \"Show the version number and quit.\"\ncomplete -c sway -s V -l verbose --description \"Enables more verbose logging.\"\ncomplete -c sway -l get-socketpath --description \"Gets the IPC socket path and prints it, then exits.\"\n\n"
  },
  {
    "path": "completions/fish/swaymsg.fish",
    "content": "# swaymsg(1) completion\n\ncomplete -f -c swaymsg\ncomplete -c swaymsg -s h -l help --description \"Show help message and quit.\"\ncomplete -c swaymsg -s m -l monitor --description \"Monitor subscribed events until killed.\"\ncomplete -c swaymsg -s p -l pretty --description \"Use pretty output even when not using a tty.\"\ncomplete -c swaymsg -s q -l quiet --description \"Sends the IPC message but does not print the response from sway.\"\ncomplete -c swaymsg -s r -l raw --description \"Use raw output even if using tty.\"\ncomplete -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).\"\ncomplete -c swaymsg -s v -l version --description \"Print the version (of swaymsg) and quit.\"\n\ncomplete -c swaymsg -s t -l type -fr --description \"Specify the type of IPC message.\"\ncomplete -c swaymsg -s t -l type -fra 'get_workspaces' --description \"Gets a JSON-encoded list of workspaces and their status.\"\ncomplete -c swaymsg -s t -l type -fra 'get_inputs' --description \"Gets a JSON-encoded list of current inputs.\"\ncomplete -c swaymsg -s t -l type -fra 'get_outputs' --description \"Gets a JSON-encoded list of current outputs.\"\ncomplete -c swaymsg -s t -l type -fra 'get_tree' --description \"Gets a JSON-encoded layout tree of all open windows, containers, outputs, workspaces, and so on.\"\ncomplete -c swaymsg -s t -l type -fra 'get_marks' --description \"Get a JSON-encoded list of marks.\"\ncomplete -c swaymsg -s t -l type -fra 'get_bar_config' --description \"Get a JSON-encoded configuration for swaybar.\"\ncomplete -c swaymsg -s t -l type -fra 'get_version' --description \"Get JSON-encoded version information for the running instance of sway.\"\ncomplete -c swaymsg -s t -l type -fra 'get_binding_modes' --description \"Gets a JSON-encoded list of currently configured binding modes.\"\ncomplete -c swaymsg -s t -l type -fra 'get_binding_state' --description \"Get JSON-encoded info about the current binding state.\"\ncomplete -c swaymsg -s t -l type -fra 'get_config' --description \"Gets a JSON-encoded copy of the current configuration.\"\ncomplete -c swaymsg -s t -l type -fra 'get_seats' --description \"Gets a JSON-encoded list of all seats, its properties and all assigned devices.\"\ncomplete -c swaymsg -s t -l type -fra 'send_tick' --description \"Sends a tick event to all subscribed clients.\"\ncomplete -c swaymsg -s t -l type -fra 'subscribe' --description \"Subscribe to a list of event types.\"\n"
  },
  {
    "path": "completions/fish/swaynag.fish",
    "content": "# swaynag\ncomplete -f -c swaynag\ncomplete -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.'\ncomplete -c swaynag -s d -l debug --description 'Enable debugging.'\ncomplete -c swaynag -s e -l edge -fr --description 'Set the edge to use: top or bottom'\ncomplete -c swaynag -s f -l font -r --description 'Set the font to use.'\ncomplete -c swaynag -s h -l help --description 'Show help message and quit.'\ncomplete -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.'\ncomplete -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.'\ncomplete -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\".'\ncomplete -c swaynag -s m -l message -fr --description 'Set the message text.'\ncomplete -c swaynag -s o -l output -fr --description 'Set the output to use.'\ncomplete -c swaynag -s s -l dismiss-button -fr --description 'Sets the text for the dismiss nagbar button. The default is \"X\".'\ncomplete -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.'\ncomplete -c swaynag -s v -l version --description 'Show the version number and quit.'\n\n# Appearance\ncomplete -c swaynag -l background -fr --description 'Set the color of the background.'\ncomplete -c swaynag -l border -fr --description 'Set the color of the border.'\ncomplete -c swaynag -l border-bottom -fr --description 'Set the color of the bottom border.'\ncomplete -c swaynag -l button-background -fr --description 'Set the color for the background for buttons.'\ncomplete -c swaynag -l text -fr --description 'Set the text color.'\ncomplete -c swaynag -l border-bottom-size -fr --description 'Set the thickness of the bottom border.'\ncomplete -c swaynag -l message-padding -fr --description 'Set the padding for the message.'\ncomplete -c swaynag -l details-border-size -fr --description 'Set the thickness for the details border.'\ncomplete -c swaynag -l button-border-size -fr --description 'Set the thickness for the button border.'\ncomplete -c swaynag -l button-gap -fr --description 'Set the size of the gap between buttons.'\ncomplete -c swaynag -l button-dismiss-gap -fr --description 'Set the size of the gap between the dismiss button and another button.'\ncomplete -c swaynag -l button-margin-right -fr --description 'Set the margin from the right of the dismiss button to edge.'\ncomplete -c swaynag -l button-padding -fr --description 'Set the padding for the button text.'\n"
  },
  {
    "path": "completions/meson.build",
    "content": "if get_option('zsh-completions')\n\tzsh_files = files(\n\t\t'zsh/_sway',\n\t\t'zsh/_swaymsg',\n\t)\n\tzsh_install_dir = join_paths(datadir, 'zsh', 'site-functions')\n\n\tinstall_data(zsh_files, install_dir: zsh_install_dir)\nendif\n\nif get_option('bash-completions')\n\tbash_comp = dependency('bash-completion', required: false)\n\n\tbash_files = files(\n\t\t'bash/sway',\n\t\t'bash/swaymsg',\n\t)\n\n\tif get_option('swaybar')\n\t\tbash_files += files('bash/swaybar')\n\tendif\n\n\tif bash_comp.found()\n\t\tbash_install_dir = bash_comp.get_variable(\n\t\t\tpkgconfig: 'completionsdir',\n\t\t\tpkgconfig_define: ['datadir', datadir]\n\t\t)\n\telse\n\t\tbash_install_dir = join_paths(datadir, 'bash-completion', 'completions')\n\tendif\n\n\tinstall_data(bash_files, install_dir: bash_install_dir)\nendif\n\nif get_option('fish-completions')\n\tfish_comp = dependency('fish', required: false)\n\n\tfish_files = files(\n\t\t'fish/sway.fish',\n\t\t'fish/swaymsg.fish',\n\t)\n\n\tif get_option('swaynag')\n\t\tfish_files += files('fish/swaynag.fish')\n\tendif\n\n\tif fish_comp.found()\n\t\tfish_install_dir = fish_comp.get_variable(\n\t\t\tpkgconfig: 'completionsdir',\n\t\t\tpkgconfig_define: ['datadir', datadir]\n\t\t)\n\telse\n\t\tfish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')\n\tendif\n\n\tinstall_data(fish_files, install_dir: fish_install_dir)\nendif\n"
  },
  {
    "path": "completions/zsh/_sway",
    "content": "#compdef sway\n#------------\n# Description\n# -----------\n#\n# Completion script for the sway window manager (http://swaywm.org)\n#\n# ---------------------------------------------\n# Author\n# -------\n#\n# * Seth Barberee <seth.barberee@gmail.com>\n#\n# -------------------------------\n_arguments -s \\\n\t'(-v --version)'{-v,--version}'[Show the version number and quit]' \\\n\t'(-h --help)'{-h,--help}'[Show help message and quit]' \\\n\t'(-c --config)'{-c,--config}'[Specify a config file]:files:_files' \\\n\t'(-C --validate)'{-C,--validate}'[Check validity of the config file, then exit]' \\\n\t'(-d --debug)'{-d,--debug}'[Enables full logging, including debug information]' \\\n\t'(-V --verbose)'{-V,--verbose}'[Enables more verbose logging]' \\\n\t'(--get-socketpath)'--get-socketpath'[Gets the IPC socket path and prints it, then exits]'\n"
  },
  {
    "path": "completions/zsh/_swaybar",
    "content": "#compdef swaybar\n#\n# Completion script for swaybar\n#\n\nlocal bars=($(swaymsg -t get_bar_config | jq -r '.[]'))\n\n_arguments -s \\\n\t'(-h --help)'{-h,--help}'[Show help message and quit]' \\\n\t'(-v --version)'{-v,--version}'[Show version and quit]' \\\n\t'(-s --socket)'{-s,--socket}'[Connect to sway via socket]:filename:_files' \\\n\t'(-b --bar_id)'{-b,--bar-id}'[Bar ID for which to get the configuration]:filename:($bars)'\\\n\t'(-d --debug)'{-d,--debug}'[Enable debugging]'\n"
  },
  {
    "path": "completions/zsh/_swaymsg",
    "content": "#compdef swaymsg\n#-----------------\n# Description\n# -----------\n#\n# Completion script for swaymsg in sway wm (http://swaywm.org)\n#\n# ------------------------------------------------------\n# Author\n# --------\n#\n#  * Seth Barberee <seth.barberee@gmail.com>\n#\n#  -------------------------------------------\n\ntypes=(\n'get_workspaces'\n'get_seats'\n'get_inputs'\n'get_outputs'\n'get_tree'\n'get_marks'\n'get_bar_config'\n'get_version'\n'get_binding_modes'\n'get_binding_state'\n'get_config'\n'send_tick'\n'subscribe'\n)\n\n_arguments -s \\\n\t'(-h --help)'{-h,--help}'[Show help message and quit]' \\\n\t'(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \\\n\t'(-p --pretty)'{-p,--pretty}'[Use pretty output even when not using a tty]' \\\n\t'(-q --quiet)'{-q,--quiet}'[Be quiet]' \\\n\t'(-r --raw)'{-r,--raw}'[Use raw output even if using a tty]' \\\n\t'(-s --socket)'{-s,--socket}'[Use the specified socket path]:files:_files' \\\n\t'(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe \"type\" types}' \\\n\t'(-v --version)'{-v,--version}'[Show the version number and quit]'\n"
  },
  {
    "path": "config.in",
    "content": "# Default config for sway\n#\n# Copy this to ~/.config/sway/config and edit it to your liking.\n#\n# Read `man 5 sway` for a complete reference.\n\n### Variables\n#\n# Logo key. Use Mod1 for Alt.\nset $mod Mod4\n# Home row direction keys, like vim\nset $left h\nset $down j\nset $up k\nset $right l\n# Your preferred terminal emulator\nset $term foot\n# Your preferred application launcher\nset $menu wmenu-run\n\n### Output configuration\n#\n# Default wallpaper (more resolutions are available in @datadir@/backgrounds/sway/)\noutput * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill\n#\n# Example configuration:\n#\n#   output HDMI-A-1 resolution 1920x1080 position 1920,0\n#\n# You can get the names of your outputs by running: swaymsg -t get_outputs\n\n### Idle configuration\n#\n# Example configuration:\n#\n# exec swayidle -w \\\n#          timeout 300 'swaylock -f -c 000000' \\\n#          timeout 600 'swaymsg \"output * power off\"' resume 'swaymsg \"output * power on\"' \\\n#          before-sleep 'swaylock -f -c 000000'\n#\n# This will lock your screen after 300 seconds of inactivity, then turn off\n# your displays after another 300 seconds, and turn your screens back on when\n# resumed. It will also lock your screen before your computer goes to sleep.\n\n### Input configuration\n#\n# Example configuration:\n#\n#   input type:touchpad {\n#       dwt enabled\n#       tap enabled\n#       natural_scroll enabled\n#       middle_emulation enabled\n#   }\n#\n#   input type:keyboard {\n#       xkb_layout \"eu\"\n#   }\n#\n# You can also configure each device individually.\n# Read `man 5 sway-input` for more information about this section.\n\n### Key bindings\n#\n# Basics:\n#\n    # Start a terminal\n    bindsym $mod+Return exec $term\n\n    # Kill focused window\n    bindsym $mod+Shift+q kill\n\n    # Start your launcher\n    bindsym $mod+d exec $menu\n\n    # Drag floating windows by holding down $mod and left mouse button.\n    # Resize them with right mouse button + $mod.\n    # Despite the name, also works for non-floating windows.\n    # Change normal to inverse to use left mouse button for resizing and right\n    # mouse button for dragging.\n    floating_modifier $mod normal\n\n    # Reload the configuration file\n    bindsym $mod+Shift+c reload\n\n    # Exit sway (logs you out of your Wayland session)\n    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'\n#\n# Moving around:\n#\n    # Move your focus around\n    bindsym $mod+$left focus left\n    bindsym $mod+$down focus down\n    bindsym $mod+$up focus up\n    bindsym $mod+$right focus right\n    # Or use $mod+[up|down|left|right]\n    bindsym $mod+Left focus left\n    bindsym $mod+Down focus down\n    bindsym $mod+Up focus up\n    bindsym $mod+Right focus right\n\n    # Move the focused window with the same, but add Shift\n    bindsym $mod+Shift+$left move left\n    bindsym $mod+Shift+$down move down\n    bindsym $mod+Shift+$up move up\n    bindsym $mod+Shift+$right move right\n    # Ditto, with arrow keys\n    bindsym $mod+Shift+Left move left\n    bindsym $mod+Shift+Down move down\n    bindsym $mod+Shift+Up move up\n    bindsym $mod+Shift+Right move right\n#\n# Workspaces:\n#\n    # Switch to workspace\n    bindsym $mod+1 workspace number 1\n    bindsym $mod+2 workspace number 2\n    bindsym $mod+3 workspace number 3\n    bindsym $mod+4 workspace number 4\n    bindsym $mod+5 workspace number 5\n    bindsym $mod+6 workspace number 6\n    bindsym $mod+7 workspace number 7\n    bindsym $mod+8 workspace number 8\n    bindsym $mod+9 workspace number 9\n    bindsym $mod+0 workspace number 10\n    # Move focused container to workspace\n    bindsym $mod+Shift+1 move container to workspace number 1\n    bindsym $mod+Shift+2 move container to workspace number 2\n    bindsym $mod+Shift+3 move container to workspace number 3\n    bindsym $mod+Shift+4 move container to workspace number 4\n    bindsym $mod+Shift+5 move container to workspace number 5\n    bindsym $mod+Shift+6 move container to workspace number 6\n    bindsym $mod+Shift+7 move container to workspace number 7\n    bindsym $mod+Shift+8 move container to workspace number 8\n    bindsym $mod+Shift+9 move container to workspace number 9\n    bindsym $mod+Shift+0 move container to workspace number 10\n    # Note: workspaces can have any name you want, not just numbers.\n    # We just use 1-10 as the default.\n#\n# Layout stuff:\n#\n    # You can \"split\" the current object of your focus with\n    # $mod+b or $mod+v, for horizontal and vertical splits\n    # respectively.\n    bindsym $mod+b splith\n    bindsym $mod+v splitv\n\n    # Switch the current container between different layout styles\n    bindsym $mod+s layout stacking\n    bindsym $mod+w layout tabbed\n    bindsym $mod+e layout toggle split\n\n    # Make the current focus fullscreen\n    bindsym $mod+f fullscreen\n\n    # Toggle the current focus between tiling and floating mode\n    bindsym $mod+Shift+space floating toggle\n\n    # Swap focus between the tiling area and the floating area\n    bindsym $mod+space focus mode_toggle\n\n    # Move focus to the parent container\n    bindsym $mod+a focus parent\n#\n# Scratchpad:\n#\n    # Sway has a \"scratchpad\", which is a bag of holding for windows.\n    # You can send windows there and get them back later.\n\n    # Move the currently focused window to the scratchpad\n    bindsym $mod+Shift+minus move scratchpad\n\n    # Show the next scratchpad window or hide the focused scratchpad window.\n    # If there are multiple scratchpad windows, this command cycles through them.\n    bindsym $mod+minus scratchpad show\n#\n# Resizing containers:\n#\nmode \"resize\" {\n    # left will shrink the containers width\n    # right will grow the containers width\n    # up will shrink the containers height\n    # down will grow the containers height\n    bindsym $left resize shrink width 10px\n    bindsym $down resize grow height 10px\n    bindsym $up resize shrink height 10px\n    bindsym $right resize grow width 10px\n\n    # Ditto, with arrow keys\n    bindsym Left resize shrink width 10px\n    bindsym Down resize grow height 10px\n    bindsym Up resize shrink height 10px\n    bindsym Right resize grow width 10px\n\n    # Return to default mode\n    bindsym Return mode \"default\"\n    bindsym Escape mode \"default\"\n}\nbindsym $mod+r mode \"resize\"\n#\n# Utilities:\n#\n    # Special keys to adjust volume via PulseAudio\n    bindsym --locked XF86AudioMute exec pactl set-sink-mute \\@DEFAULT_SINK@ toggle\n    bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \\@DEFAULT_SINK@ -5%\n    bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \\@DEFAULT_SINK@ +5%\n    bindsym --locked XF86AudioMicMute exec pactl set-source-mute \\@DEFAULT_SOURCE@ toggle\n\n    # Special keys to control media via playerctl\n    bindsym --locked XF86AudioPlay exec playerctl play-pause\n    bindsym --locked XF86AudioPause exec playerctl play-pause\n    bindsym --locked XF86AudioPrev exec playerctl previous\n    bindsym --locked XF86AudioNext exec playerctl next\n    bindsym --locked XF86AudioStop exec playerctl stop\n\n    # Special keys to adjust brightness via brightnessctl\n    bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%-\n    bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+\n\n    # Special key to take a screenshot with grim\n    bindsym Print exec grim\n\n#\n# Status Bar:\n#\n# Read `man 5 sway-bar` for more information about this section.\nbar {\n    position top\n\n    # When the status_command prints a new line to stdout, swaybar updates.\n    # The default just shows the current date and time.\n    status_command while date +'%Y-%m-%d %X'; do sleep 1; done\n\n    colors {\n        statusline #ffffff\n        background #323232\n        inactive_workspace #32323200 #32323200 #5c5c5c\n    }\n}\n\ninclude @sysconfdir@/sway/config.d/*\n"
  },
  {
    "path": "include/cairo_util.h",
    "content": "#ifndef _SWAY_CAIRO_UTIL_H\n#define _SWAY_CAIRO_UTIL_H\n#include \"config.h\"\n#include <stdint.h>\n#include <cairo.h>\n#include <wayland-client-protocol.h>\n\nvoid cairo_set_source_u32(cairo_t *cairo, uint32_t color);\ncairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel);\n\ncairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,\n\t\tint width, int height);\n\n#endif\n"
  },
  {
    "path": "include/gesture.h",
    "content": "#ifndef _SWAY_GESTURE_H\n#define _SWAY_GESTURE_H\n\n#include <stdbool.h>\n#include <stdint.h>\n\n/**\n * A gesture type used in binding.\n */\nenum gesture_type {\n\tGESTURE_TYPE_NONE = 0,\n\tGESTURE_TYPE_HOLD,\n\tGESTURE_TYPE_PINCH,\n\tGESTURE_TYPE_SWIPE,\n};\n\n// Turns single type enum value to constant string representation.\nconst char *gesture_type_string(enum gesture_type direction);\n\n// Value to use to accept any finger count\nextern const uint8_t GESTURE_FINGERS_ANY;\n\n/**\n * A gesture direction used in binding.\n */\nenum gesture_direction {\n\tGESTURE_DIRECTION_NONE = 0,\n\t// Directions based on delta x and y\n\tGESTURE_DIRECTION_UP = 1 << 0,\n\tGESTURE_DIRECTION_DOWN = 1 << 1,\n\tGESTURE_DIRECTION_LEFT = 1 << 2,\n\tGESTURE_DIRECTION_RIGHT = 1 << 3,\n\t// Directions based on scale\n\tGESTURE_DIRECTION_INWARD = 1 << 4,\n\tGESTURE_DIRECTION_OUTWARD = 1 << 5,\n\t// Directions based on rotation\n\tGESTURE_DIRECTION_CLOCKWISE = 1 << 6,\n\tGESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7,\n};\n\n// Turns single direction enum value to constant string representation.\nconst char *gesture_direction_string(enum gesture_direction direction);\n\n/**\n * Struct representing a pointer gesture\n */\nstruct gesture {\n\tenum gesture_type type;\n\tuint8_t fingers;\n\tuint32_t directions;\n};\n\n/**\n * Parses gesture from <gesture>[:<fingers>][:<directions>] string.\n *\n * Return NULL on success, otherwise error message string\n */\nchar *gesture_parse(const char *input, struct gesture *output);\n\n// Turns gesture into string representation\nchar *gesture_to_string(struct gesture *gesture);\n\n// Check if gesture is of certain type and finger count.\nbool gesture_check(struct gesture *target,\n\t\tenum gesture_type type, uint8_t fingers);\n\n// Check if a gesture target/binding is match by other gesture/input\nbool gesture_match(struct gesture *target,\n\t\tstruct gesture *to_match, bool exact);\n\n// Returns true if gesture are exactly the same\nbool gesture_equal(struct gesture *a, struct gesture *b);\n\n// Compare distance between two matched target gestures.\nint8_t gesture_compare(struct gesture *a, struct gesture *b);\n\n// Small helper struct to track gestures over time\nstruct gesture_tracker {\n\tenum gesture_type type;\n\tuint8_t fingers;\n\tdouble dx, dy;\n\tdouble scale;\n\tdouble rotation;\n};\n\n// Begin gesture tracking\nvoid gesture_tracker_begin(struct gesture_tracker *tracker,\n\t\tenum gesture_type type, uint8_t fingers);\n\n// Check if the provides type is currently being tracked\nbool gesture_tracker_check(struct gesture_tracker *tracker,\n\t\tenum gesture_type type);\n\n// Update gesture track with new data point\nvoid gesture_tracker_update(struct gesture_tracker *tracker, double dx,\n\t\tdouble dy, double scale, double rotation);\n\n// Reset tracker\nvoid gesture_tracker_cancel(struct gesture_tracker *tracker);\n\n// Reset tracker and return gesture tracked\nstruct gesture *gesture_tracker_end(struct gesture_tracker *tracker);\n\n#endif\n"
  },
  {
    "path": "include/ipc-client.h",
    "content": "#ifndef _SWAY_IPC_CLIENT_H\n#define _SWAY_IPC_CLIENT_H\n\n// arbitrary number, it's probably sufficient, higher number = more memory usage\n#define JSON_MAX_DEPTH 512\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <sys/time.h>\n\n#include \"ipc.h\"\n\n/**\n * IPC response including type of IPC response, size of payload and the json\n * encoded payload string.\n */\nstruct ipc_response {\n\tuint32_t size;\n\tuint32_t type;\n\tchar *payload;\n};\n\n/**\n * Gets the path to the IPC socket from sway.\n */\nchar *get_socketpath(void);\n/**\n * Opens the sway socket.\n */\nint ipc_open_socket(const char *socket_path);\n/**\n * Issues a single IPC command and returns the buffer. len will be updated with\n * the length of the buffer returned from sway.\n */\nchar *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len);\n/**\n * Receives a single IPC response and returns an ipc_response.\n */\nstruct ipc_response *ipc_recv_response(int socketfd);\n/**\n * Free ipc_response struct\n */\nvoid free_ipc_response(struct ipc_response *response);\n/**\n * Sets the receive timeout for the IPC socket\n */\nbool ipc_set_recv_timeout(int socketfd, struct timeval tv);\n\n#endif\n"
  },
  {
    "path": "include/ipc.h",
    "content": "#ifndef _SWAY_IPC_H\n#define _SWAY_IPC_H\n\n#define event_mask(ev) (1 << (ev & 0x7F))\n\nenum ipc_command_type {\n\t// i3 command types - see i3's I3_REPLY_TYPE constants\n\tIPC_COMMAND = 0,\n\tIPC_GET_WORKSPACES = 1,\n\tIPC_SUBSCRIBE = 2,\n\tIPC_GET_OUTPUTS = 3,\n\tIPC_GET_TREE = 4,\n\tIPC_GET_MARKS = 5,\n\tIPC_GET_BAR_CONFIG = 6,\n\tIPC_GET_VERSION = 7,\n\tIPC_GET_BINDING_MODES = 8,\n\tIPC_GET_CONFIG = 9,\n\tIPC_SEND_TICK = 10,\n\tIPC_SYNC = 11,\n\tIPC_GET_BINDING_STATE = 12,\n\n\t// sway-specific command types\n\tIPC_GET_INPUTS = 100,\n\tIPC_GET_SEATS = 101,\n\n\t// Events sent from sway to clients. Events have the highest bits set.\n\tIPC_EVENT_WORKSPACE = ((1<<31) | 0),\n\tIPC_EVENT_OUTPUT = ((1<<31) | 1),\n\tIPC_EVENT_MODE = ((1<<31) | 2),\n\tIPC_EVENT_WINDOW = ((1<<31) | 3),\n\tIPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4),\n\tIPC_EVENT_BINDING = ((1<<31) | 5),\n\tIPC_EVENT_SHUTDOWN = ((1<<31) | 6),\n\tIPC_EVENT_TICK = ((1<<31) | 7),\n\n\t// sway-specific event types\n\tIPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20),\n\tIPC_EVENT_INPUT = ((1<<31) | 21),\n};\n\n#endif\n"
  },
  {
    "path": "include/list.h",
    "content": "#ifndef _SWAY_LIST_H\n#define _SWAY_LIST_H\n\ntypedef struct {\n\tint capacity;\n\tint length;\n\tvoid **items;\n} list_t;\n\nlist_t *create_list(void);\nvoid list_free(list_t *list);\nvoid list_add(list_t *list, void *item);\nvoid list_insert(list_t *list, int index, void *item);\nvoid list_del(list_t *list, int index);\nvoid list_cat(list_t *list, list_t *source);\n// See qsort. Remember to use *_qsort functions as compare functions,\n// because they dereference the left and right arguments first!\nvoid list_qsort(list_t *list, int compare(const void *left, const void *right));\n// Return index for first item in list that returns 0 for given compare\n// function or -1 if none matches.\nint list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to), const void *cmp_to);\nint list_find(list_t *list, const void *item);\n// stable sort since qsort is not guaranteed to be stable\nvoid list_stable_sort(list_t *list, int compare(const void *a, const void *b));\n// swap two elements in a list\nvoid list_swap(list_t *list, int src, int dest);\n// move item to end of list\nvoid list_move_to_end(list_t *list, void *item);\n\n/* Calls `free` for each item in the list, then frees the list.\n * Do not use this to free lists of primitives or items that require more\n * complicated deallocation code.\n */\nvoid list_free_items_and_destroy(list_t *list);\n#endif\n"
  },
  {
    "path": "include/log.h",
    "content": "#ifndef _SWAY_LOG_H\n#define _SWAY_LOG_H\n\n#include <stdbool.h>\n#include <stdarg.h>\n#include <string.h>\n#include <errno.h>\n\ntypedef enum {\n\tSWAY_SILENT = 0,\n\tSWAY_ERROR = 1,\n\tSWAY_INFO = 2,\n\tSWAY_DEBUG = 3,\n\tSWAY_LOG_IMPORTANCE_LAST,\n} sway_log_importance_t;\n\n#ifdef __GNUC__\n#define ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end)))\n#else\n#define ATTRIB_PRINTF(start, end)\n#endif\n\nvoid error_handler(int sig);\n\ntypedef void (*terminate_callback_t)(int exit_code);\n\n// Will log all messages less than or equal to `verbosity`\n// The `terminate` callback is called by `sway_abort`\nvoid sway_log_init(sway_log_importance_t verbosity, terminate_callback_t terminate);\n\nvoid _sway_log(sway_log_importance_t verbosity, const char *format, ...) ATTRIB_PRINTF(2, 3);\nvoid _sway_vlog(sway_log_importance_t verbosity, const char *format, va_list args) ATTRIB_PRINTF(2, 0);\nvoid _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);\nbool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);\n\n#ifdef SWAY_REL_SRC_DIR\n// strip prefix from __FILE__, leaving the path relative to the project root\n#define _SWAY_FILENAME ((const char *)__FILE__ + sizeof(SWAY_REL_SRC_DIR) - 1)\n#else\n#define _SWAY_FILENAME __FILE__\n#endif\n\n#define sway_log(verb, fmt, ...) \\\n\t_sway_log(verb, \"[%s:%d] \" fmt, _SWAY_FILENAME, __LINE__, ##__VA_ARGS__)\n\n#define sway_vlog(verb, fmt, args) \\\n\t_sway_vlog(verb, \"[%s:%d] \" fmt, _SWAY_FILENAME, __LINE__, args)\n\n#define sway_log_errno(verb, fmt, ...) \\\n\tsway_log(verb, fmt \": %s\", ##__VA_ARGS__, strerror(errno))\n\n#define sway_abort(FMT, ...) \\\n\t_sway_abort(\"[%s:%d] \" FMT, _SWAY_FILENAME, __LINE__, ##__VA_ARGS__)\n\n#define sway_assert(COND, FMT, ...) \\\n\t_sway_assert(COND, \"[%s:%d] %s:\" FMT, _SWAY_FILENAME, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)\n\n#endif\n"
  },
  {
    "path": "include/loop.h",
    "content": "#ifndef _SWAY_LOOP_H\n#define _SWAY_LOOP_H\n#include <stdbool.h>\n\n/**\n * This is an event loop system designed for sway clients, not sway itself.\n *\n * The loop consists of file descriptors and timers. Typically the Wayland\n * display's file descriptor will be one of the fds in the loop.\n */\n\nstruct loop;\nstruct loop_timer;\n\n/**\n * Create an event loop.\n */\nstruct loop *loop_create(void);\n\n/**\n * Destroy the event loop (eg. on program termination).\n */\nvoid loop_destroy(struct loop *loop);\n\n/**\n * Poll the event loop. This will block until one of the fds has data.\n */\nvoid loop_poll(struct loop *loop);\n\n/**\n * Add a file descriptor to the loop.\n */\nvoid loop_add_fd(struct loop *loop, int fd, short mask,\n\t\tvoid (*func)(int fd, short mask, void *data), void *data);\n\n/**\n * Add a timer to the loop.\n *\n * When the timer expires, the timer will be removed from the loop and freed.\n */\nstruct loop_timer *loop_add_timer(struct loop *loop, int ms,\n\t\tvoid (*callback)(void *data), void *data);\n\n/**\n * Remove a file descriptor from the loop.\n */\nbool loop_remove_fd(struct loop *loop, int fd);\n\n/**\n * Remove a timer from the loop.\n */\nbool loop_remove_timer(struct loop *loop, struct loop_timer *timer);\n\n#endif\n"
  },
  {
    "path": "include/meson.build",
    "content": "configure_file(output: 'config.h',  configuration: conf_data)\n"
  },
  {
    "path": "include/pango.h",
    "content": "#ifndef _SWAY_PANGO_H\n#define _SWAY_PANGO_H\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <cairo.h>\n#include <pango/pangocairo.h>\n#include \"stringop.h\"\n\n/**\n * Utility function which escape characters a & < > ' \".\n *\n * The function returns the length of the escaped string, optionally writing the\n * escaped string to dest if provided.\n */\nsize_t escape_markup_text(const char *src, char *dest);\nPangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,\n\t\tconst char *text, double scale, bool markup);\nvoid get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,\n\t\tint *baseline, double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(8, 9);\nvoid get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline);\nvoid render_text(cairo_t *cairo, PangoFontDescription *desc,\n\t\tdouble scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6);\n\n#endif\n"
  },
  {
    "path": "include/pool-buffer.h",
    "content": "#ifndef _SWAY_BUFFERS_H\n#define _SWAY_BUFFERS_H\n#include <cairo.h>\n#include <pango/pangocairo.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <wayland-client.h>\n\nstruct pool_buffer {\n\tstruct wl_buffer *buffer;\n\tcairo_surface_t *surface;\n\tcairo_t *cairo;\n\tPangoContext *pango;\n\tuint32_t width, height;\n\tvoid *data;\n\tsize_t size;\n\tbool busy;\n};\n\nstruct pool_buffer *get_next_buffer(struct wl_shm *shm,\n\t\tstruct pool_buffer pool[static 2], uint32_t width, uint32_t height);\nvoid destroy_buffer(struct pool_buffer *buffer);\n\n#endif\n"
  },
  {
    "path": "include/stringop.h",
    "content": "#ifndef _SWAY_STRINGOP_H\n#define _SWAY_STRINGOP_H\n\n#include <stdbool.h>\n#include <stddef.h>\n#include \"list.h\"\n\n#ifdef __GNUC__\n#define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end)))\n#else\n#define _SWAY_ATTRIB_PRINTF(start, end)\n#endif\n\nvoid strip_whitespace(char *str);\nvoid strip_quotes(char *str);\n\n// strcat that does nothing if dest or src is NULL\nchar *lenient_strcat(char *dest, const char *src);\nchar *lenient_strncat(char *dest, const char *src, size_t len);\n\n// strcmp that also handles null pointers.\nint lenient_strcmp(const char *a, const char *b);\n\n// Simply split a string with delims, free with `list_free_items_and_destroy`\nlist_t *split_string(const char *str, const char *delims);\n\n// Splits an argument string, keeping quotes intact\nchar **split_args(const char *str, int *argc);\nvoid free_argv(int argc, char **argv);\n\nint unescape_string(char *string);\nchar *join_args(char **argv, int argc);\n\n// Split string into 2 by delim, handle quotes\nchar *argsep(char **stringp, const char *delim, char *matched_delim);\n\n// Expand a path using shell replacements such as $HOME and ~\nbool expand_path(char **path);\n\nchar *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0);\nchar *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2);\n\nbool has_prefix(const char *str, const char *prefix);\n\n#endif\n"
  },
  {
    "path": "include/sway/commands.h",
    "content": "#ifndef _SWAY_COMMANDS_H\n#define _SWAY_COMMANDS_H\n\n#include <wlr/util/edges.h>\n#include \"config.h\"\n#include \"stringop.h\"\n\nstruct sway_container;\n\ntypedef struct cmd_results *sway_cmd(int argc, char **argv);\n\nstruct cmd_handler {\n\tconst char *command;\n\tsway_cmd *handle;\n};\n\n/**\n * Indicates the result of a command's execution.\n */\nenum cmd_status {\n\tCMD_SUCCESS, \t\t/**< The command was successful */\n\tCMD_FAILURE,\t\t/**< The command resulted in an error */\n\tCMD_INVALID, \t\t/**< Unknown command or parser error */\n\tCMD_DEFER,\t\t/**< Command execution deferred */\n\tCMD_BLOCK,\n\tCMD_BLOCK_COMMANDS,\n\tCMD_BLOCK_END\n};\n\n/**\n * Stores the result of executing a command.\n */\nstruct cmd_results {\n\tenum cmd_status status;\n\t/**\n\t * Human friendly error message, or NULL on success\n\t */\n\tchar *error;\n};\n\nenum expected_args {\n\tEXPECTED_AT_LEAST,\n\tEXPECTED_AT_MOST,\n\tEXPECTED_EQUAL_TO\n};\n\nstruct cmd_results *checkarg(int argc, const char *name,\n\t\tenum expected_args type, int val);\n\nconst struct cmd_handler *find_handler(const char *line,\n\t\tconst struct cmd_handler *cmd_handlers, size_t handlers_size);\n\n/**\n * Parse and executes a command.\n *\n * If the command string contains criteria then the command will be executed on\n * all matching containers. Otherwise, it'll run on the `con` container. If\n * `con` is NULL then it'll run on the currently focused container.\n */\nlist_t *execute_command(char *command,  struct sway_seat *seat,\n\t\tstruct sway_container *con);\n/**\n * Parse and handles a command during config file loading.\n *\n * Do not use this under normal conditions.\n */\nstruct cmd_results *config_command(char *command, char **new_block);\n/**\n * Parse and handle a sub command\n */\nstruct cmd_results *config_subcommand(char **argv, int argc,\n\t\tconst struct cmd_handler *handlers, size_t handlers_size);\n/*\n * Parses a command policy rule.\n */\nstruct cmd_results *config_commands_command(char *exec);\n/**\n * Allocates a cmd_results object.\n */\nstruct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...) _SWAY_ATTRIB_PRINTF(2, 3);\n/**\n * Frees a cmd_results object.\n */\nvoid free_cmd_results(struct cmd_results *results);\n/**\n * Serializes a list of cmd_results to a JSON string.\n *\n * Free the JSON string later on.\n */\nchar *cmd_results_to_json(list_t *res_list);\n\n/**\n * TODO: Move this function and its dependent functions to container.c.\n */\nvoid container_resize_tiled(struct sway_container *parent, uint32_t axis,\n\t\tint amount);\n\nstruct sway_container *container_find_resize_parent(struct sway_container *con,\n\t\tuint32_t edge);\n\n/**\n * Handlers shared by exec and exec_always.\n */\nsway_cmd cmd_exec_validate;\nsway_cmd cmd_exec_process;\n\nsway_cmd cmd_allow_tearing;\nsway_cmd cmd_assign;\nsway_cmd cmd_bar;\nsway_cmd cmd_bindcode;\nsway_cmd cmd_bindgesture;\nsway_cmd cmd_bindswitch;\nsway_cmd cmd_bindsym;\nsway_cmd cmd_border;\nsway_cmd cmd_client_noop;\nsway_cmd cmd_client_focused;\nsway_cmd cmd_client_focused_inactive;\nsway_cmd cmd_client_focused_tab_title;\nsway_cmd cmd_client_unfocused;\nsway_cmd cmd_client_urgent;\nsway_cmd cmd_client_placeholder;\nsway_cmd cmd_client_background;\nsway_cmd cmd_commands;\nsway_cmd cmd_create_output;\nsway_cmd cmd_default_border;\nsway_cmd cmd_default_floating_border;\nsway_cmd cmd_default_orientation;\nsway_cmd cmd_exec;\nsway_cmd cmd_exec_always;\nsway_cmd cmd_exit;\nsway_cmd cmd_floating;\nsway_cmd cmd_floating_maximum_size;\nsway_cmd cmd_floating_minimum_size;\nsway_cmd cmd_floating_modifier;\nsway_cmd cmd_floating_scroll;\nsway_cmd cmd_focus;\nsway_cmd cmd_focus_follows_mouse;\nsway_cmd cmd_focus_on_window_activation;\nsway_cmd cmd_focus_wrapping;\nsway_cmd cmd_font;\nsway_cmd cmd_for_window;\nsway_cmd cmd_force_display_urgency_hint;\nsway_cmd cmd_force_focus_wrapping;\nsway_cmd cmd_fullscreen;\nsway_cmd cmd_gaps;\nsway_cmd cmd_hide_edge_borders;\nsway_cmd cmd_include;\nsway_cmd cmd_inhibit_idle;\nsway_cmd cmd_input;\nsway_cmd cmd_seat;\nsway_cmd cmd_ipc;\nsway_cmd cmd_kill;\nsway_cmd cmd_layout;\nsway_cmd cmd_log_colors;\nsway_cmd cmd_mark;\nsway_cmd cmd_max_render_time;\nsway_cmd cmd_mode;\nsway_cmd cmd_mouse_warping;\nsway_cmd cmd_move;\nsway_cmd cmd_new_float;\nsway_cmd cmd_new_window;\nsway_cmd cmd_nop;\nsway_cmd cmd_opacity;\nsway_cmd cmd_no_focus;\nsway_cmd cmd_output;\nsway_cmd cmd_permit;\nsway_cmd cmd_popup_during_fullscreen;\nsway_cmd cmd_primary_selection;\nsway_cmd cmd_reject;\nsway_cmd cmd_reload;\nsway_cmd cmd_rename;\nsway_cmd cmd_resize;\nsway_cmd cmd_scratchpad;\nsway_cmd cmd_seamless_mouse;\nsway_cmd cmd_set;\nsway_cmd cmd_shortcuts_inhibitor;\nsway_cmd cmd_show_marks;\nsway_cmd cmd_smart_borders;\nsway_cmd cmd_smart_gaps;\nsway_cmd cmd_split;\nsway_cmd cmd_splith;\nsway_cmd cmd_splitt;\nsway_cmd cmd_splitv;\nsway_cmd cmd_sticky;\nsway_cmd cmd_swaybg_command;\nsway_cmd cmd_swaynag_command;\nsway_cmd cmd_swap;\nsway_cmd cmd_tiling_drag;\nsway_cmd cmd_tiling_drag_threshold;\nsway_cmd cmd_title_align;\nsway_cmd cmd_title_format;\nsway_cmd cmd_titlebar_border_thickness;\nsway_cmd cmd_titlebar_padding;\nsway_cmd cmd_unbindcode;\nsway_cmd cmd_unbindswitch;\nsway_cmd cmd_unbindgesture;\nsway_cmd cmd_unbindsym;\nsway_cmd cmd_unmark;\nsway_cmd cmd_urgent;\nsway_cmd cmd_workspace;\nsway_cmd cmd_workspace_layout;\nsway_cmd cmd_ws_auto_back_and_forth;\nsway_cmd cmd_xwayland;\n\nsway_cmd bar_cmd_bindcode;\nsway_cmd bar_cmd_binding_mode_indicator;\nsway_cmd bar_cmd_bindsym;\nsway_cmd bar_cmd_colors;\nsway_cmd bar_cmd_font;\nsway_cmd bar_cmd_gaps;\nsway_cmd bar_cmd_mode;\nsway_cmd bar_cmd_modifier;\nsway_cmd bar_cmd_output;\nsway_cmd bar_cmd_height;\nsway_cmd bar_cmd_hidden_state;\nsway_cmd bar_cmd_icon_theme;\nsway_cmd bar_cmd_id;\nsway_cmd bar_cmd_position;\nsway_cmd bar_cmd_separator_symbol;\nsway_cmd bar_cmd_status_command;\nsway_cmd bar_cmd_status_edge_padding;\nsway_cmd bar_cmd_status_padding;\nsway_cmd bar_cmd_pango_markup;\nsway_cmd bar_cmd_strip_workspace_numbers;\nsway_cmd bar_cmd_strip_workspace_name;\nsway_cmd bar_cmd_swaybar_command;\nsway_cmd bar_cmd_tray_bindcode;\nsway_cmd bar_cmd_tray_bindsym;\nsway_cmd bar_cmd_tray_output;\nsway_cmd bar_cmd_tray_padding;\nsway_cmd bar_cmd_unbindcode;\nsway_cmd bar_cmd_unbindsym;\nsway_cmd bar_cmd_wrap_scroll;\nsway_cmd bar_cmd_workspace_buttons;\nsway_cmd bar_cmd_workspace_min_width;\n\nsway_cmd bar_colors_cmd_active_workspace;\nsway_cmd bar_colors_cmd_background;\nsway_cmd bar_colors_cmd_focused_background;\nsway_cmd bar_colors_cmd_binding_mode;\nsway_cmd bar_colors_cmd_focused_workspace;\nsway_cmd bar_colors_cmd_inactive_workspace;\nsway_cmd bar_colors_cmd_separator;\nsway_cmd bar_colors_cmd_focused_separator;\nsway_cmd bar_colors_cmd_statusline;\nsway_cmd bar_colors_cmd_focused_statusline;\nsway_cmd bar_colors_cmd_urgent_workspace;\n\nsway_cmd input_cmd_seat;\nsway_cmd input_cmd_accel_profile;\nsway_cmd input_cmd_calibration_matrix;\nsway_cmd input_cmd_click_method;\nsway_cmd input_cmd_clickfinger_button_map;\nsway_cmd input_cmd_drag;\nsway_cmd input_cmd_drag_lock;\nsway_cmd input_cmd_dwt;\nsway_cmd input_cmd_dwtp;\nsway_cmd input_cmd_events;\nsway_cmd input_cmd_left_handed;\nsway_cmd input_cmd_map_from_region;\nsway_cmd input_cmd_map_to_output;\nsway_cmd input_cmd_map_to_region;\nsway_cmd input_cmd_middle_emulation;\nsway_cmd input_cmd_natural_scroll;\nsway_cmd input_cmd_pointer_accel;\nsway_cmd input_cmd_rotation_angle;\nsway_cmd input_cmd_scroll_factor;\nsway_cmd input_cmd_repeat_delay;\nsway_cmd input_cmd_repeat_rate;\nsway_cmd input_cmd_scroll_button;\nsway_cmd input_cmd_scroll_button_lock;\nsway_cmd input_cmd_scroll_method;\nsway_cmd input_cmd_tap;\nsway_cmd input_cmd_tap_button_map;\nsway_cmd input_cmd_tool_mode;\nsway_cmd input_cmd_xkb_capslock;\nsway_cmd input_cmd_xkb_file;\nsway_cmd input_cmd_xkb_layout;\nsway_cmd input_cmd_xkb_model;\nsway_cmd input_cmd_xkb_numlock;\nsway_cmd input_cmd_xkb_options;\nsway_cmd input_cmd_xkb_rules;\nsway_cmd input_cmd_xkb_switch_layout;\nsway_cmd input_cmd_xkb_variant;\n\nsway_cmd output_cmd_adaptive_sync;\nsway_cmd output_cmd_allow_tearing;\nsway_cmd output_cmd_background;\nsway_cmd output_cmd_color_profile;\nsway_cmd output_cmd_disable;\nsway_cmd output_cmd_dpms;\nsway_cmd output_cmd_enable;\nsway_cmd output_cmd_hdr;\nsway_cmd output_cmd_max_render_time;\nsway_cmd output_cmd_mode;\nsway_cmd output_cmd_modeline;\nsway_cmd output_cmd_position;\nsway_cmd output_cmd_power;\nsway_cmd output_cmd_render_bit_depth;\nsway_cmd output_cmd_scale;\nsway_cmd output_cmd_scale_filter;\nsway_cmd output_cmd_subpixel;\nsway_cmd output_cmd_toggle;\nsway_cmd output_cmd_transform;\nsway_cmd output_cmd_unplug;\n\nsway_cmd seat_cmd_attach;\nsway_cmd seat_cmd_cursor;\nsway_cmd seat_cmd_fallback;\nsway_cmd seat_cmd_hide_cursor;\nsway_cmd seat_cmd_idle_inhibit;\nsway_cmd seat_cmd_idle_wake;\nsway_cmd seat_cmd_keyboard_grouping;\nsway_cmd seat_cmd_pointer_constraint;\nsway_cmd seat_cmd_shortcuts_inhibitor;\nsway_cmd seat_cmd_xcursor_theme;\n\nsway_cmd cmd_ipc_cmd;\nsway_cmd cmd_ipc_events;\nsway_cmd cmd_ipc_event_cmd;\n\n#endif\n"
  },
  {
    "path": "include/sway/config.h",
    "content": "#ifndef _SWAY_CONFIG_H\n#define _SWAY_CONFIG_H\n#include <libinput.h>\n#include <stdint.h>\n#include <string.h>\n#include <time.h>\n#include <wlr/interfaces/wlr_switch.h>\n#include <wlr/types/wlr_tablet_tool.h>\n#include <wlr/util/box.h>\n#include <wlr/render/color.h>\n#include <xkbcommon/xkbcommon.h>\n#include <xf86drmMode.h>\n#include \"../include/config.h\"\n#include \"gesture.h\"\n#include \"list.h\"\n#include \"stringop.h\"\n#include \"swaynag.h\"\n#include \"tree/container.h\"\n#include \"sway/input/tablet.h\"\n#include \"sway/tree/root.h\"\n#include \"wlr-layer-shell-unstable-v1-protocol.h\"\n#include <pango/pangocairo.h>\n\n// TODO: Refactor this shit\n\n/**\n * Describes a variable created via the `set` command.\n */\nstruct sway_variable {\n\tchar *name;\n\tchar *value;\n};\n\nenum binding_input_type {\n\tBINDING_KEYCODE,\n\tBINDING_KEYSYM,\n\tBINDING_MOUSECODE,\n\tBINDING_MOUSESYM,\n\tBINDING_SWITCH, // dummy, only used to call seat_execute_command\n\tBINDING_GESTURE // dummy, only used to call seat_execute_command\n};\n\nenum binding_flags {\n\tBINDING_RELEASE = 1 << 0,\n\tBINDING_LOCKED = 1 << 1, // keyboard only\n\tBINDING_BORDER = 1 << 2, // mouse only; trigger on container border\n\tBINDING_CONTENTS = 1 << 3, // mouse only; trigger on container contents\n\tBINDING_TITLEBAR = 1 << 4, // mouse only; trigger on container titlebar\n\tBINDING_CODE = 1 << 5, // keyboard only; convert keysyms into keycodes\n\tBINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload\n\tBINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor\n\tBINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key\n\tBINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match\n};\n\n/**\n * A key (or mouse) binding and an associated command.\n */\nstruct sway_binding {\n\tenum binding_input_type type;\n\tint order;\n\tchar *input;\n\tuint32_t flags;\n\tlist_t *keys; // sorted in ascending order\n\tlist_t *syms; // sorted in ascending order; NULL if BINDING_CODE is not set\n\tuint32_t modifiers;\n\txkb_layout_index_t group;\n\tchar *command;\n};\n\nenum sway_switch_trigger {\n\tSWAY_SWITCH_TRIGGER_OFF,\n\tSWAY_SWITCH_TRIGGER_ON,\n\tSWAY_SWITCH_TRIGGER_TOGGLE,\n};\n\n/**\n * A laptop switch binding and an associated command.\n */\nstruct sway_switch_binding {\n\tenum wlr_switch_type type;\n\tenum sway_switch_trigger trigger;\n\tuint32_t flags;\n\tchar *command;\n};\n\n/**\n * A gesture binding and an associated command.\n */\nstruct sway_gesture_binding {\n\tchar *input;\n\tuint32_t flags;\n\tstruct gesture gesture;\n\tchar *command;\n};\n\n/**\n * Focus on window activation.\n */\nenum sway_fowa {\n\tFOWA_SMART,\n\tFOWA_URGENT,\n\tFOWA_FOCUS,\n\tFOWA_NONE,\n};\n\n/**\n * A \"mode\" of keybindings created via the `mode` command.\n */\nstruct sway_mode {\n\tchar *name;\n\tlist_t *keysym_bindings;\n\tlist_t *keycode_bindings;\n\tlist_t *mouse_bindings;\n\tlist_t *switch_bindings;\n\tlist_t *gesture_bindings;\n\tbool pango;\n};\n\nstruct input_config_mapped_from_region {\n\tdouble x1, y1;\n\tdouble x2, y2;\n\tbool mm;\n};\n\nstruct calibration_matrix {\n\tbool configured;\n\tfloat matrix[6];\n};\n\nenum input_config_mapped_to {\n\tMAPPED_TO_DEFAULT,\n\tMAPPED_TO_OUTPUT,\n\tMAPPED_TO_REGION,\n};\n\nstruct input_config_tool {\n\tenum wlr_tablet_tool_type type;\n\tenum sway_tablet_tool_mode mode;\n};\n\n/**\n * options for input devices\n */\nstruct input_config {\n\tchar *identifier;\n\tconst char *input_type;\n\n\tint accel_profile;\n\tstruct calibration_matrix calibration_matrix;\n\tint click_method;\n\tint clickfinger_button_map;\n\tint drag;\n\tint drag_lock;\n\tint dwt;\n\tint dwtp;\n\tint left_handed;\n\tint middle_emulation;\n\tint natural_scroll;\n\tfloat pointer_accel;\n\tfloat rotation_angle;\n\tfloat scroll_factor;\n\tint repeat_delay;\n\tint repeat_rate;\n\tint scroll_button;\n\tint scroll_button_lock;\n\tint scroll_method;\n\tint send_events;\n\tint tap;\n\tint tap_button_map;\n\n\tchar *xkb_layout;\n\tchar *xkb_model;\n\tchar *xkb_options;\n\tchar *xkb_rules;\n\tchar *xkb_variant;\n\tchar *xkb_file;\n\n\tbool xkb_file_is_set;\n\n\tint xkb_numlock;\n\tint xkb_capslock;\n\n\tstruct input_config_mapped_from_region *mapped_from_region;\n\n\tenum input_config_mapped_to mapped_to;\n\tchar *mapped_to_output;\n\tstruct wlr_box *mapped_to_region;\n\n\tlist_t *tools;\n\n\tbool capturable;\n\tstruct wlr_box region;\n};\n\n/**\n * Options for misc device configurations that happen in the seat block\n */\nstruct seat_attachment_config {\n\tchar *identifier;\n\t// TODO other things are configured here for some reason\n};\n\nenum seat_config_hide_cursor_when_typing {\n\tHIDE_WHEN_TYPING_DEFAULT, // the default is currently disabled\n\tHIDE_WHEN_TYPING_ENABLE,\n\tHIDE_WHEN_TYPING_DISABLE,\n};\n\nenum seat_config_allow_constrain {\n\tCONSTRAIN_DEFAULT, // the default is currently enabled\n\tCONSTRAIN_ENABLE,\n\tCONSTRAIN_DISABLE,\n};\n\nenum seat_config_shortcuts_inhibit {\n\tSHORTCUTS_INHIBIT_DEFAULT, // the default is currently enabled\n\tSHORTCUTS_INHIBIT_ENABLE,\n\tSHORTCUTS_INHIBIT_DISABLE,\n};\n\nenum seat_keyboard_grouping {\n\tKEYBOARD_GROUP_DEFAULT, // the default is currently smart\n\tKEYBOARD_GROUP_NONE,\n\tKEYBOARD_GROUP_SMART, // keymap and repeat info\n};\n\nenum sway_input_idle_source {\n\tIDLE_SOURCE_KEYBOARD = 1 << 0,\n\tIDLE_SOURCE_POINTER = 1 << 1,\n\tIDLE_SOURCE_TOUCH = 1 << 2,\n\tIDLE_SOURCE_TABLET_PAD = 1 << 3,\n\tIDLE_SOURCE_TABLET_TOOL = 1 << 4,\n\tIDLE_SOURCE_SWITCH = 1 << 5,\n};\n\n/**\n * Options for multiseat and other misc device configurations\n */\nstruct seat_config {\n\tchar *name;\n\tint fallback; // -1 means not set\n\tlist_t *attachments; // list of seat_attachment configs\n\tint hide_cursor_timeout;\n\tenum seat_config_hide_cursor_when_typing hide_cursor_when_typing;\n\tenum seat_config_allow_constrain allow_constrain;\n\tenum seat_config_shortcuts_inhibit shortcuts_inhibit;\n\tenum seat_keyboard_grouping keyboard_grouping;\n\tuint32_t idle_inhibit_sources, idle_wake_sources;\n\tstruct {\n\t\tchar *name;\n\t\tint size;\n\t} xcursor_theme;\n};\n\nenum scale_filter_mode {\n\tSCALE_FILTER_DEFAULT, // the default is currently smart\n\tSCALE_FILTER_LINEAR,\n\tSCALE_FILTER_NEAREST,\n\tSCALE_FILTER_SMART,\n};\n\nenum render_bit_depth {\n\tRENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 for SDR, 10 for HDR\n\tRENDER_BIT_DEPTH_6,\n\tRENDER_BIT_DEPTH_8,\n\tRENDER_BIT_DEPTH_10,\n};\n\nenum color_profile {\n\tCOLOR_PROFILE_DEFAULT, // default is Transform with NULL color_transform\n\tCOLOR_PROFILE_TRANSFORM, // use color_transform from output_config\n\tCOLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES, // create transform from wlr_output\n};\n\n/**\n * Size and position configuration for a particular output.\n *\n * This is set via the `output` command.\n */\nstruct output_config {\n\tchar *name;\n\tint enabled;\n\tint power;\n\tint width, height;\n\tfloat refresh_rate;\n\tint custom_mode;\n\tdrmModeModeInfo drm_mode;\n\tint x, y;\n\tfloat scale;\n\tenum scale_filter_mode scale_filter;\n\tint32_t transform;\n\tenum wl_output_subpixel subpixel;\n\tint max_render_time; // In milliseconds\n\tint adaptive_sync;\n\tenum render_bit_depth render_bit_depth;\n\tenum color_profile color_profile;\n\tstruct wlr_color_transform *color_transform;\n\tint allow_tearing;\n\tint hdr;\n\n\tchar *background;\n\tchar *background_option;\n\tchar *background_fallback;\n};\n\n/**\n * Stores size of gaps for each side\n */\nstruct side_gaps {\n\tint top;\n\tint right;\n\tint bottom;\n\tint left;\n};\n\nenum smart_gaps_mode {\n\tSMART_GAPS_OFF,\n\tSMART_GAPS_ON,\n\tSMART_GAPS_INVERSE_OUTER,\n};\n\n/**\n * Stores configuration for a workspace, regardless of whether the workspace\n * exists.\n */\nstruct workspace_config {\n\tchar *workspace;\n\tlist_t *outputs;\n\tint gaps_inner;\n\tstruct side_gaps gaps_outer;\n};\n\nenum pango_markup_config {\n\tPANGO_MARKUP_DISABLED = false,\n\tPANGO_MARKUP_ENABLED = true,\n\tPANGO_MARKUP_DEFAULT // The default is font dependent (\"pango:\" prefix)\n};\n\nstruct bar_config {\n\tchar *swaybar_command;\n\tstruct wl_client *client;\n\tstruct wl_listener client_destroy;\n\n\t/**\n\t * One of \"dock\", \"hide\", \"invisible\"\n\t *\n\t * Always visible in dock mode. Visible only when modifier key is held in hide mode.\n\t * Never visible in invisible mode.\n\t */\n\tchar *mode;\n\t/**\n\t * One of \"show\" or \"hide\".\n\t *\n\t * In \"show\" mode, it will always be shown on top of the active workspace.\n\t */\n\tchar *hidden_state;\n\tbool visible_by_modifier; // only relevant in \"hide\" mode\n\t/**\n\t * Id name used to identify the bar through IPC.\n\t *\n\t * Defaults to bar-x, where x corresponds to the position of the\n\t * embedding bar block in the config file (bar-0, bar-1, ...).\n\t */\n\tchar *id;\n\tuint32_t modifier;\n\tlist_t *outputs;\n\tchar *position;\n\tlist_t *bindings;\n\tchar *status_command;\n\tenum pango_markup_config pango_markup;\n\tchar *font;\n\tint height; // -1 not defined\n\tbool workspace_buttons;\n\tbool wrap_scroll;\n\tchar *separator_symbol;\n\tbool strip_workspace_numbers;\n\tbool strip_workspace_name;\n\tbool binding_mode_indicator;\n\tbool verbose;\n\tstruct side_gaps gaps;\n\tint status_padding;\n\tint status_edge_padding;\n\tuint32_t workspace_min_width;\n\tstruct {\n\t\tchar *background;\n\t\tchar *statusline;\n\t\tchar *separator;\n\t\tchar *focused_background;\n\t\tchar *focused_statusline;\n\t\tchar *focused_separator;\n\t\tchar *focused_workspace_border;\n\t\tchar *focused_workspace_bg;\n\t\tchar *focused_workspace_text;\n\t\tchar *active_workspace_border;\n\t\tchar *active_workspace_bg;\n\t\tchar *active_workspace_text;\n\t\tchar *inactive_workspace_border;\n\t\tchar *inactive_workspace_bg;\n\t\tchar *inactive_workspace_text;\n\t\tchar *urgent_workspace_border;\n\t\tchar *urgent_workspace_bg;\n\t\tchar *urgent_workspace_text;\n\t\tchar *binding_mode_border;\n\t\tchar *binding_mode_bg;\n\t\tchar *binding_mode_text;\n\t} colors;\n\n#if HAVE_TRAY\n\tchar *icon_theme;\n\tstruct wl_list tray_bindings; // struct tray_binding::link\n\tlist_t *tray_outputs; // char *\n\tint tray_padding;\n#endif\n};\n\nstruct bar_binding {\n\tuint32_t button;\n\tbool release;\n\tchar *command;\n};\n\n#if HAVE_TRAY\nstruct tray_binding {\n\tuint32_t button;\n\tconst char *command;\n\tstruct wl_list link; // struct tray_binding::link\n};\n#endif\n\nstruct border_colors {\n\tfloat border[4];\n\tfloat background[4];\n\tfloat text[4];\n\tfloat indicator[4];\n\tfloat child_border[4];\n};\n\nenum edge_border_types {\n\tE_NONE, /**< Don't hide edge borders */\n\tE_VERTICAL, /**< hide vertical edge borders */\n\tE_HORIZONTAL, /**< hide horizontal edge borders */\n\tE_BOTH, /**< hide vertical and horizontal edge borders */\n};\n\nenum edge_border_smart_types {\n\tESMART_OFF,\n\tESMART_ON, /**< hide edges if precisely one window is present in workspace */\n\tESMART_NO_GAPS, /**< hide edges if one window and gaps to edge is zero */\n};\n\nenum sway_popup_during_fullscreen {\n\tPOPUP_SMART,\n\tPOPUP_IGNORE,\n\tPOPUP_LEAVE,\n};\n\nenum focus_follows_mouse_mode {\n\tFOLLOWS_NO,\n\tFOLLOWS_YES,\n\tFOLLOWS_ALWAYS,\n};\n\nenum focus_wrapping_mode {\n\tWRAP_NO,\n\tWRAP_YES,\n\tWRAP_FORCE,\n\tWRAP_WORKSPACE,\n};\n\nenum mouse_warping_mode {\n\tWARP_NO,\n\tWARP_OUTPUT,\n\tWARP_CONTAINER,\n};\n\nenum alignment {\n\tALIGN_LEFT,\n\tALIGN_CENTER,\n\tALIGN_RIGHT,\n};\n\nenum xwayland_mode {\n\tXWAYLAND_MODE_DISABLED,\n\tXWAYLAND_MODE_LAZY,\n\tXWAYLAND_MODE_IMMEDIATE,\n};\n\n/**\n * The configuration struct. The result of loading a config file.\n */\nstruct sway_config {\n\tchar *swaynag_command;\n\tstruct swaynag_instance swaynag_config_errors;\n\tlist_t *symbols;\n\tlist_t *modes;\n\tlist_t *bars;\n\tlist_t *cmd_queue;\n\tlist_t *workspace_configs;\n\tlist_t *output_configs;\n\tlist_t *input_configs;\n\tlist_t *input_type_configs;\n\tlist_t *seat_configs;\n\tlist_t *criteria;\n\tlist_t *no_focus;\n\tlist_t *active_bar_modifiers;\n\tstruct sway_mode *current_mode;\n\tstruct bar_config *current_bar;\n\tuint32_t floating_mod;\n\tbool floating_mod_inverse;\n\tuint32_t dragging_key;\n\tuint32_t resizing_key;\n\tchar *floating_scroll_up_cmd;\n\tchar *floating_scroll_down_cmd;\n\tchar *floating_scroll_left_cmd;\n\tchar *floating_scroll_right_cmd;\n\tenum sway_container_layout default_orientation;\n\tenum sway_container_layout default_layout;\n\tchar *font; // Used for IPC.\n\tPangoFontDescription *font_description; // Used internally for rendering and validating.\n\tint font_height;\n\tint font_baseline;\n\tbool pango_markup;\n\tint titlebar_border_thickness;\n\tint titlebar_h_padding;\n\tint titlebar_v_padding;\n\tsize_t urgent_timeout;\n\tenum sway_fowa focus_on_window_activation;\n\tenum sway_popup_during_fullscreen popup_during_fullscreen;\n\tenum xwayland_mode xwayland;\n\n\t// swaybg\n\tchar *swaybg_command;\n\tstruct wl_client *swaybg_client;\n\tstruct wl_listener swaybg_client_destroy;\n\n\t// Flags\n\tenum focus_follows_mouse_mode focus_follows_mouse;\n\tenum mouse_warping_mode mouse_warping;\n\tenum focus_wrapping_mode focus_wrapping;\n\tbool active;\n\tbool failed;\n\tbool reloading;\n\tbool reading;\n\tbool validating;\n\tbool auto_back_and_forth;\n\tbool show_marks;\n\tenum alignment title_align;\n\tbool primary_selection;\n\n\tbool tiling_drag;\n\tint tiling_drag_threshold;\n\n\tenum smart_gaps_mode smart_gaps;\n\tint gaps_inner;\n\tstruct side_gaps gaps_outer;\n\n\tlist_t *config_chain;\n\tbool user_config_path;\n\tconst char *current_config_path;\n\tconst char *current_config;\n\tint current_config_line_number;\n\tchar *current_config_line;\n\n\tenum sway_container_border border;\n\tenum sway_container_border floating_border;\n\tint border_thickness;\n\tint floating_border_thickness;\n\tenum edge_border_types hide_edge_borders;\n\tenum edge_border_smart_types hide_edge_borders_smart;\n\tbool hide_lone_tab;\n\n\t// border colors\n\tstruct {\n\t\tstruct border_colors focused;\n\t\tstruct border_colors focused_inactive;\n\t\tstruct border_colors focused_tab_title;\n\t\tstruct border_colors unfocused;\n\t\tstruct border_colors urgent;\n\t\tstruct border_colors placeholder;\n\t\tfloat background[4];\n\t} border_colors;\n\n\tbool has_focused_tab_title;\n\n\t// floating view\n\tint32_t floating_maximum_width;\n\tint32_t floating_maximum_height;\n\tint32_t floating_minimum_width;\n\tint32_t floating_minimum_height;\n\n\t// The keysym to keycode translation\n\tstruct xkb_state *keysym_translation_state;\n\n\t// Context for command handlers\n\tstruct {\n\t\tstruct input_config *input_config;\n\t\tstruct output_config *output_config;\n\t\tstruct seat_config *seat_config;\n\t\tstruct sway_seat *seat;\n\t\tstruct sway_node *node;\n\t\tstruct sway_container *container;\n\t\tstruct sway_workspace *workspace;\n\t\tbool node_overridden; // True if the node is selected by means other than focus\n\t\tstruct {\n\t\t\tint argc;\n\t\t\tchar **argv;\n\t\t} leftovers;\n\t} handler_context;\n};\n\n/**\n * Loads the main config from the given path. is_active should be true when\n * reloading the config.\n */\nbool load_main_config(const char *path, bool is_active, bool validating);\n\n/**\n * Loads an included config. Can only be used after load_main_config.\n */\nvoid load_include_configs(const char *path, struct sway_config *config,\n\t\tstruct swaynag_instance *swaynag);\n\n/**\n * Reads the config from the given FILE.\n */\nbool read_config(FILE *file, struct sway_config *config,\n\t\tstruct swaynag_instance *swaynag);\n\n/**\n * Run the commands that were deferred when reading the config file.\n */\nvoid run_deferred_commands(void);\n\n/**\n * Run the binding commands that were deferred when initializing the inputs\n */\nvoid run_deferred_bindings(void);\n\n/**\n * Adds a warning entry to the swaynag instance used for errors.\n */\nvoid config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2);\n\n/**\n * Free config struct\n */\nvoid free_config(struct sway_config *config);\n\nvoid free_sway_variable(struct sway_variable *var);\n\n/**\n * Does variable replacement for a string based on the config's currently loaded variables.\n */\nchar *do_var_replacement(char *str);\n\nint input_identifier_cmp(const void *item, const void *data);\n\nstruct input_config *new_input_config(const char* identifier);\n\nvoid merge_input_config(struct input_config *dst, struct input_config *src);\n\nstruct input_config *store_input_config(struct input_config *ic, char **error);\n\nvoid input_config_fill_rule_names(struct input_config *ic,\n\t\tstruct xkb_rule_names *rules);\n\nvoid free_input_config(struct input_config *ic);\n\nint seat_name_cmp(const void *item, const void *data);\n\nstruct seat_config *new_seat_config(const char* name);\n\nvoid merge_seat_config(struct seat_config *dst, struct seat_config *src);\n\nstruct seat_config *copy_seat_config(struct seat_config *seat);\n\nvoid free_seat_config(struct seat_config *ic);\n\nstruct seat_attachment_config *seat_attachment_config_new(void);\n\nstruct seat_attachment_config *seat_config_get_attachment(\n\t\tstruct seat_config *seat_config, char *identifier);\n\nstruct seat_config *store_seat_config(struct seat_config *seat);\n\nint output_name_cmp(const void *item, const void *data);\n\nvoid output_get_identifier(char *identifier, size_t len,\n\tstruct sway_output *output);\n\nconst char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter);\n\nstruct output_config *new_output_config(const char *name);\n\nbool apply_output_configs(struct output_config **ocs, size_t ocs_len,\n\t\tbool test_only, bool degrade_to_off);\n\nvoid apply_stored_output_configs(void);\n\n/**\n * store_output_config stores a new output config. An output may be matched by\n * three different config types, in order of precedence: Identifier, name and\n * wildcard. When storing a config type of lower precedence, assume that the\n * user wants the config to take immediate effect by superseding (clearing) the\n * same values from higher presedence configuration.\n */\nvoid store_output_config(struct output_config *oc);\n\nstruct output_config *find_output_config(struct sway_output *output);\n\nvoid free_output_config(struct output_config *oc);\n\nvoid request_modeset(void);\nvoid force_modeset(void);\nbool modeset_is_pending(void);\n\nbool spawn_swaybg(void);\n\nint workspace_output_cmp_workspace(const void *a, const void *b);\n\nvoid free_sway_binding(struct sway_binding *sb);\n\nvoid free_switch_binding(struct sway_switch_binding *binding);\n\nvoid free_gesture_binding(struct sway_gesture_binding *binding);\n\nvoid seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);\n\nvoid load_swaybar(struct bar_config *bar);\n\nvoid load_swaybars(void);\n\nstruct bar_config *default_bar_config(void);\n\nvoid free_bar_config(struct bar_config *bar);\n\nvoid free_bar_binding(struct bar_binding *binding);\n\nvoid free_workspace_config(struct workspace_config *wsc);\n\n/**\n * Updates the value of config->font_height based on the metrics for title's\n * font as reported by pango.\n *\n * If the height has changed, all containers will be rearranged to take on the\n * new size.\n */\nvoid config_update_font_height(void);\n\n/**\n * Convert bindsym into bindcode using the first configured layout.\n * Return false in case the conversion is unsuccessful.\n */\nbool translate_binding(struct sway_binding *binding);\n\nvoid translate_keysyms(struct input_config *input_config);\n\nvoid binding_add_translated(struct sway_binding *binding, list_t *bindings);\n\n/* Global config singleton. */\nextern struct sway_config *config;\n\n#endif\n"
  },
  {
    "path": "include/sway/criteria.h",
    "content": "#ifndef _SWAY_CRITERIA_H\n#define _SWAY_CRITERIA_H\n\n#define PCRE2_CODE_UNIT_WIDTH 8\n#include <pcre2.h>\n#include \"config.h\"\n#include \"list.h\"\n#include \"tree/view.h\"\n\n#if WLR_HAS_XWAYLAND\n#include \"sway/xwayland.h\"\n#endif\n\nenum criteria_type {\n\tCT_COMMAND                 = 1 << 0,\n\tCT_ASSIGN_OUTPUT           = 1 << 1,\n\tCT_ASSIGN_WORKSPACE        = 1 << 2,\n\tCT_ASSIGN_WORKSPACE_NUMBER = 1 << 3,\n\tCT_NO_FOCUS                = 1 << 4,\n};\n\nenum pattern_type {\n\tPATTERN_PCRE2,\n\tPATTERN_FOCUSED,\n};\n\nstruct pattern {\n\tenum pattern_type match_type;\n\tpcre2_code *regex;\n};\n\nstruct criteria {\n\tenum criteria_type type;\n\tchar *raw; // entire criteria string (for logging)\n\tchar *cmdlist;\n\tchar *target; // workspace or output name for `assign` criteria\n\n\tstruct pattern *title;\n\tstruct pattern *shell;\n\tstruct pattern *app_id;\n\tstruct pattern *con_mark;\n\tuint32_t con_id; // internal ID\n#if WLR_HAS_XWAYLAND\n\tstruct pattern *class;\n\tuint32_t id; // X11 window ID\n\tstruct pattern *instance;\n\tstruct pattern *window_role;\n\tenum atom_name window_type;\n#endif\n\tbool all;\n\tbool floating;\n\tbool tiling;\n\tchar urgent; // 'l' for latest or 'o' for oldest\n\tstruct pattern *workspace;\n\tpid_t pid;\n\tstruct pattern *sandbox_engine;\n\tstruct pattern *sandbox_app_id;\n\tstruct pattern *sandbox_instance_id;\n\tstruct pattern *tag;\n};\n\nbool criteria_is_empty(struct criteria *criteria);\nbool criteria_is_equal(struct criteria *left, struct criteria *right);\n\nbool criteria_already_exists(struct criteria *criteria);\n\nvoid criteria_destroy(struct criteria *criteria);\n\n/**\n * Generate a criteria struct from a raw criteria string such as\n * [class=\"foo\" instance=\"bar\"] (brackets inclusive).\n *\n * The error argument is expected to be an address of a null pointer. If an\n * error is encountered, the function will return NULL and the pointer will be\n * changed to point to the error string. This string should be freed afterwards.\n */\nstruct criteria *criteria_parse(char *raw, char **error);\n\n/**\n * Compile a list of criterias matching the given view.\n *\n * Criteria types can be bitwise ORed.\n */\nlist_t *criteria_for_view(struct sway_view *view, enum criteria_type types);\n\n/**\n * Compile a list of containers matching the given criteria.\n */\nlist_t *criteria_get_containers(struct criteria *criteria);\n\n#endif\n"
  },
  {
    "path": "include/sway/decoration.h",
    "content": "#ifndef _SWAY_DECORATION_H\n#define _SWAY_DECORATION_H\n\n#include <wlr/types/wlr_server_decoration.h>\n\nstruct sway_server_decoration {\n\tstruct wlr_server_decoration *wlr_server_decoration;\n\tstruct wl_list link;\n\n\tstruct wl_listener destroy;\n\tstruct wl_listener mode;\n};\n\nstruct sway_server_decoration *decoration_from_surface(\n\tstruct wlr_surface *surface);\n\n#endif\n"
  },
  {
    "path": "include/sway/desktop/idle_inhibit_v1.h",
    "content": "#ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H\n#define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H\n#include <wlr/types/wlr_idle_inhibit_v1.h>\n\nenum sway_idle_inhibit_mode {\n\tINHIBIT_IDLE_APPLICATION,  // Application set inhibitor (when visible)\n\tINHIBIT_IDLE_FOCUS,  // User set inhibitor when focused\n\tINHIBIT_IDLE_FULLSCREEN,  // User set inhibitor when fullscreen + visible\n\tINHIBIT_IDLE_OPEN,  // User set inhibitor while open\n\tINHIBIT_IDLE_VISIBLE  // User set inhibitor when visible\n};\n\nstruct sway_idle_inhibit_manager_v1 {\n\tstruct wlr_idle_inhibit_manager_v1 *wlr_manager;\n\tstruct wl_listener new_idle_inhibitor_v1;\n\tstruct wl_listener manager_destroy;\n\tstruct wl_list inhibitors;\n};\n\nstruct sway_idle_inhibitor_v1 {\n\tstruct wlr_idle_inhibitor_v1 *wlr_inhibitor;\n\tstruct sway_view *view;\n\tenum sway_idle_inhibit_mode mode;\n\n\tstruct wl_list link;\n\tstruct wl_listener destroy;\n};\n\nbool sway_idle_inhibit_v1_is_active(\n\tstruct sway_idle_inhibitor_v1 *inhibitor);\n\nvoid sway_idle_inhibit_v1_check_active(void);\n\nvoid sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view,\n\t\tenum sway_idle_inhibit_mode mode);\n\nstruct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(\n\t\tstruct sway_view *view);\n\nstruct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view(\n\t\tstruct sway_view *view);\n\nvoid sway_idle_inhibit_v1_user_inhibitor_destroy(\n\t\tstruct sway_idle_inhibitor_v1 *inhibitor);\n\nbool sway_idle_inhibit_manager_v1_init(void);\n\n#endif\n"
  },
  {
    "path": "include/sway/desktop/launcher.h",
    "content": "#ifndef _SWAY_LAUNCHER_H\n#define _SWAY_LAUNCHER_H\n\n#include <stdlib.h>\n#include <wayland-server-core.h>\n#include \"sway/input/seat.h\"\n\nstruct launcher_ctx {\n\tpid_t pid;\n\tchar *fallback_name;\n\tstruct wlr_xdg_activation_token_v1 *token;\n\tstruct wl_listener token_destroy;\n\tstruct sway_seat *seat;\n\tstruct wl_listener seat_destroy;\n\n\tbool activated;\n\tbool had_focused_surface;\n\n\tstruct sway_node *node;\n\tstruct wl_listener node_destroy;\n\n\tstruct wl_list link; // sway_server::pending_launcher_ctxs\n};\n\nstruct launcher_ctx *launcher_ctx_find_pid(pid_t pid);\n\nstruct sway_workspace *launcher_ctx_get_workspace(struct launcher_ctx *ctx);\n\nvoid launcher_ctx_consume(struct launcher_ctx *ctx);\n\nvoid launcher_ctx_destroy(struct launcher_ctx *ctx);\n\nstruct launcher_ctx *launcher_ctx_create_internal(void);\n\nstruct launcher_ctx *launcher_ctx_create(\n\tstruct wlr_xdg_activation_token_v1 *token, struct sway_node *node);\n\nconst char *launcher_ctx_get_token_name(struct launcher_ctx *ctx);\n\n#endif\n"
  },
  {
    "path": "include/sway/desktop/transaction.h",
    "content": "#ifndef _SWAY_TRANSACTION_H\n#define _SWAY_TRANSACTION_H\n#include <stdint.h>\n#include <stdbool.h>\n#include <wlr/types/wlr_scene.h>\n\n/**\n * Transactions enable us to perform atomic layout updates.\n *\n * A transaction contains a list of containers and their new state.\n * A state might contain a new size, or new border settings, or new parent/child\n * relationships.\n *\n * Committing a transaction makes sway notify of all the affected clients with\n * their new sizes. We then wait for all the views to respond with their new\n * surface sizes. When all are ready, or when a timeout has passed, we apply the\n * updates all at the same time.\n *\n * When we want to make adjustments to the layout, we change the pending state\n * in containers, mark them as dirty and call transaction_commit_dirty(). This\n * create and commits a transaction from the dirty containers.\n */\n\nstruct sway_transaction_instruction;\nstruct sway_view;\n\n/**\n * Find all dirty containers, create and commit a transaction containing them,\n * and unmark them as dirty.\n */\nvoid transaction_commit_dirty(void);\n\n/*\n * Same as transaction_commit_dirty, but signalling that this is a\n * client-initiated change has already taken effect.\n */\nvoid transaction_commit_dirty_client(void);\n\n/**\n * Notify the transaction system that a view is ready for the new layout.\n *\n * When all views in the transaction are ready, the layout will be applied.\n *\n * A success boolean is returned denoting that this part of the transaction is\n * ready.\n */\nbool transaction_notify_view_ready_by_serial(struct sway_view *view,\n\t\tuint32_t serial);\n\n/**\n * Notify the transaction system that a view is ready for the new layout, but\n * identifying the instruction by geometry rather than by serial.\n *\n * This is used by xwayland views, as they don't have serials.\n *\n * A success boolean is returned denoting that this part of the transaction is\n * ready.\n */\nbool transaction_notify_view_ready_by_geometry(struct sway_view *view,\n\t\tdouble x, double y, int width, int height);\n\nvoid arrange_popups(struct wlr_scene_tree *popups);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/cursor.h",
    "content": "#ifndef _SWAY_INPUT_CURSOR_H\n#define _SWAY_INPUT_CURSOR_H\n#include <stdbool.h>\n#include <stdint.h>\n#include <wlr/types/wlr_pointer_constraints_v1.h>\n#include <wlr/types/wlr_pointer_gestures_v1.h>\n#include <wlr/types/wlr_compositor.h>\n#include \"sway/input/seat.h\"\n#include \"config.h\"\n\n#define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32\n\n#define SWAY_SCROLL_UP KEY_MAX + 1\n#define SWAY_SCROLL_DOWN KEY_MAX + 2\n#define SWAY_SCROLL_LEFT KEY_MAX + 3\n#define SWAY_SCROLL_RIGHT KEY_MAX + 4\n\nstruct sway_cursor {\n\tstruct sway_seat *seat;\n\tstruct wlr_cursor *cursor;\n\tstruct {\n\t\tdouble x, y;\n\t\tstruct sway_node *node;\n\t} previous;\n\tstruct wlr_xcursor_manager *xcursor_manager;\n\tstruct wl_list tablets;\n\tstruct wl_list tablet_pads;\n\n\tconst char *image;\n\tstruct wl_client *image_client;\n\tstruct wlr_surface *image_surface;\n\tint hotspot_x, hotspot_y;\n\n\tstruct wlr_pointer_constraint_v1 *active_constraint;\n\tpixman_region32_t confine; // invalid if active_constraint == NULL\n\tbool active_confine_requires_warp;\n\n\tstruct wl_listener hold_begin;\n\tstruct wl_listener hold_end;\n\tstruct wl_listener pinch_begin;\n\tstruct wl_listener pinch_update;\n\tstruct wl_listener pinch_end;\n\tstruct wl_listener swipe_begin;\n\tstruct wl_listener swipe_update;\n\tstruct wl_listener swipe_end;\n\n\tstruct wl_listener motion;\n\tstruct wl_listener motion_absolute;\n\tstruct wl_listener button;\n\tstruct wl_listener axis;\n\tstruct wl_listener frame;\n\n\tstruct wl_listener touch_down;\n\tstruct wl_listener touch_up;\n\tstruct wl_listener touch_cancel;\n\tstruct wl_listener touch_motion;\n\tstruct wl_listener touch_frame;\n\tbool simulating_pointer_from_touch;\n\tbool pointer_touch_up;\n\tint32_t pointer_touch_id;\n\n\tstruct wl_listener tool_axis;\n\tstruct wl_listener tool_tip;\n\tstruct wl_listener tool_proximity;\n\tstruct wl_listener tool_button;\n\tbool simulating_pointer_from_tool_tip;\n\tbool simulating_pointer_from_tool_button;\n\tuint32_t tool_buttons;\n\n\tstruct wl_listener request_set_cursor;\n\tstruct wl_listener image_surface_destroy;\n\n\tstruct wl_listener constraint_commit;\n\n\tstruct wl_event_source *hide_source;\n\tbool hidden;\n\t// This field is just a cache of the field in seat_config in order to avoid\n\t// costly seat_config lookups on every keypress. HIDE_WHEN_TYPING_DEFAULT\n\t// indicates that there is no cached value.\n\tenum seat_config_hide_cursor_when_typing hide_when_typing;\n\n\tsize_t pressed_button_count;\n};\n\nstruct sway_node;\n\nstruct sway_node *node_at_coords(\n\t\tstruct sway_seat *seat, double lx, double ly,\n\t\tstruct wlr_surface **surface, double *sx, double *sy);\n\nvoid sway_cursor_destroy(struct sway_cursor *cursor);\nstruct sway_cursor *sway_cursor_create(struct sway_seat *seat);\n\n/**\n * \"Rebase\" a cursor on top of whatever view is underneath it.\n *\n * This chooses a cursor icon and sends a motion event to the surface.\n */\nvoid cursor_rebase(struct sway_cursor *cursor);\nvoid cursor_rebase_all(void);\nvoid cursor_update_image(struct sway_cursor *cursor, struct sway_node *node);\n\nvoid cursor_handle_activity_from_idle_source(struct sway_cursor *cursor,\n\t\tenum sway_input_idle_source idle_source);\nvoid cursor_handle_activity_from_device(struct sway_cursor *cursor,\n\t\tstruct wlr_input_device *device);\nvoid cursor_unhide(struct sway_cursor *cursor);\nint cursor_get_timeout(struct sway_cursor *cursor);\nvoid cursor_notify_key_press(struct sway_cursor *cursor);\n\nvoid pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, double dx, double dy,\n\t\tdouble dx_unaccel, double dy_unaccel);\n\nvoid dispatch_cursor_button(struct sway_cursor *cursor,\n\tstruct wlr_input_device *device, uint32_t time_msec, uint32_t button,\n\tenum wl_pointer_button_state state);\n\nvoid dispatch_cursor_axis(struct sway_cursor *cursor,\n\t\tstruct wlr_pointer_axis_event *event);\n\nvoid cursor_set_image(struct sway_cursor *cursor, const char *image,\n\tstruct wl_client *client);\n\nvoid cursor_set_image_surface(struct sway_cursor *cursor,\n\t\tstruct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y,\n\t\tstruct wl_client *client);\n\nvoid cursor_warp_to_container(struct sway_cursor *cursor,\n\tstruct sway_container *container, bool force);\n\nvoid cursor_warp_to_workspace(struct sway_cursor *cursor,\n\t\tstruct sway_workspace *workspace);\n\n\nvoid sway_cursor_constrain(struct sway_cursor *cursor,\n\tstruct wlr_pointer_constraint_v1 *constraint);\n\nuint32_t get_mouse_bindsym(const char *name, char **error);\n\nuint32_t get_mouse_bindcode(const char *name, char **error);\n\n// Considers both bindsym and bindcode\nuint32_t get_mouse_button(const char *name, char **error);\n\nconst char *get_mouse_button_name(uint32_t button);\n\nvoid handle_request_set_cursor_shape(struct wl_listener *listener, void *data);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/input-manager.h",
    "content": "#ifndef _SWAY_INPUT_INPUT_MANAGER_H\n#define _SWAY_INPUT_INPUT_MANAGER_H\n#include <libinput.h>\n#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>\n#include <wlr/types/wlr_virtual_keyboard_v1.h>\n#include <wlr/types/wlr_virtual_pointer_v1.h>\n#include <wlr/types/wlr_transient_seat_v1.h>\n#include \"sway/config.h\"\n#include \"list.h\"\n\nstruct sway_server;\n\nstruct sway_input_device {\n\tchar *identifier;\n\tstruct wlr_input_device *wlr_device;\n\tstruct wl_list link;\n\tstruct wl_listener device_destroy;\n\tbool is_virtual;\n};\n\nstruct sway_input_manager {\n\tstruct wl_list devices;\n\tstruct wl_list seats;\n\n\tstruct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit;\n\tstruct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;\n\tstruct wlr_virtual_pointer_manager_v1 *virtual_pointer;\n\tstruct wlr_pointer_gestures_v1 *pointer_gestures;\n\tstruct wlr_transient_seat_manager_v1 *transient_seat_manager;\n\n\tstruct wl_listener new_input;\n\tstruct wl_listener inhibit_activate;\n\tstruct wl_listener inhibit_deactivate;\n\tstruct wl_listener keyboard_shortcuts_inhibit_new_inhibitor;\n\tstruct wl_listener virtual_keyboard_new;\n\tstruct wl_listener virtual_pointer_new;\n\tstruct wl_listener transient_seat_create;\n};\n\nstruct sway_input_manager *input_manager_create(struct sway_server *server);\n\nvoid input_manager_finish(struct sway_input_manager *input);\n\nbool input_manager_has_focus(struct sway_node *node);\n\nvoid input_manager_set_focus(struct sway_node *node);\n\nvoid input_manager_configure_xcursor(void);\n\nvoid input_manager_apply_input_config(struct input_config *input_config);\n\nvoid input_manager_configure_all_input_mappings(void);\n\nvoid input_manager_reset_input(struct sway_input_device *input_device);\n\nvoid input_manager_reset_all_inputs(void);\n\nvoid input_manager_apply_seat_config(struct seat_config *seat_config);\n\nstruct sway_seat *input_manager_get_default_seat(void);\n\nstruct sway_seat *input_manager_get_seat(const char *seat_name, bool create);\n\n/**\n * If none of the seat configs have a fallback setting (either true or false),\n * create the default seat (if needed) and set it as the fallback\n */\nvoid input_manager_verify_fallback_seat(void);\n\n/**\n * Gets the last seat the user interacted with\n */\nstruct sway_seat *input_manager_current_seat(void);\n\nstruct input_config *input_device_get_config(struct sway_input_device *device);\n\nchar *input_device_get_identifier(struct wlr_input_device *device);\n\nconst char *input_device_get_type(struct sway_input_device *device);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/keyboard.h",
    "content": "#ifndef _SWAY_INPUT_KEYBOARD_H\n#define _SWAY_INPUT_KEYBOARD_H\n\n#include \"sway/input/seat.h\"\n\n#define SWAY_KEYBOARD_PRESSED_KEYS_CAP 32\n\n/**\n * Get modifier mask from modifier name.\n *\n * Returns the modifier mask or 0 if the name isn't found.\n */\nuint32_t get_modifier_mask_by_name(const char *name);\n\n/**\n * Get modifier name from modifier mask.\n *\n * Returns the modifier name or NULL if it isn't found.\n */\nconst char *get_modifier_name_by_mask(uint32_t modifier);\n\n/**\n * Get an array of modifier names from modifier_masks\n *\n * Populates the names array and return the number of names added.\n */\nint get_modifier_names(const char **names, uint32_t modifier_masks);\n\nstruct sway_shortcut_state {\n\t/**\n\t * A list of pressed key ids (either keysyms or keycodes),\n\t * including duplicates when different keycodes produce the same key id.\n\t *\n\t * Each key id is associated with the keycode (in `pressed_keycodes`)\n\t * whose press generated it, so that the key id can be removed on\n\t * keycode release without recalculating the transient link between\n\t * keycode and key id at the time of the key press.\n\t */\n\tuint32_t pressed_keys[SWAY_KEYBOARD_PRESSED_KEYS_CAP];\n\t/**\n\t * The list of keycodes associated to currently pressed key ids,\n\t * including duplicates when a keycode generates multiple key ids.\n\t */\n\tuint32_t pressed_keycodes[SWAY_KEYBOARD_PRESSED_KEYS_CAP];\n\tuint32_t last_keycode;\n\tuint32_t last_raw_modifiers;\n\tsize_t npressed;\n\tuint32_t current_key;\n};\n\nstruct sway_keyboard {\n\tstruct sway_seat_device *seat_device;\n\tstruct wlr_keyboard *wlr;\n\n\tstruct xkb_keymap *keymap;\n\txkb_layout_index_t effective_layout;\n\n\tint32_t repeat_rate;\n\tint32_t repeat_delay;\n\n\tstruct wl_listener keyboard_key;\n\tstruct wl_listener keyboard_modifiers;\n\n\tstruct sway_shortcut_state state_keysyms_translated;\n\tstruct sway_shortcut_state state_keysyms_raw;\n\tstruct sway_shortcut_state state_keycodes;\n\tstruct sway_shortcut_state state_pressed_sent;\n\tstruct sway_binding *held_binding;\n\n\tstruct wl_event_source *key_repeat_source;\n\tstruct sway_binding *repeat_binding;\n};\n\nstruct sway_keyboard_group {\n\tstruct wlr_keyboard_group *wlr_group;\n\tstruct sway_seat_device *seat_device;\n\tstruct wl_listener keyboard_key;\n\tstruct wl_listener keyboard_modifiers;\n\tstruct wl_listener enter;\n\tstruct wl_listener leave;\n\tstruct wl_list link; // sway_seat::keyboard_groups\n};\n\nstruct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,\n\t\tchar **error);\n\nstruct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device);\n\nvoid sway_keyboard_configure(struct sway_keyboard *keyboard);\n\nvoid sway_keyboard_destroy(struct sway_keyboard *keyboard);\n\nvoid sway_keyboard_disarm_key_repeat(struct sway_keyboard *keyboard);\n#endif\n"
  },
  {
    "path": "include/sway/input/libinput.h",
    "content": "#ifndef _SWAY_INPUT_LIBINPUT_H\n#define _SWAY_INPUT_LIBINPUT_H\n#include \"sway/input/input-manager.h\"\n\nbool sway_input_configure_libinput_device(struct sway_input_device *device);\n\nvoid sway_input_configure_libinput_device_send_events(\n\tstruct sway_input_device *device);\n\nvoid sway_input_reset_libinput_device(struct sway_input_device *device);\n\nbool sway_libinput_device_is_builtin(struct sway_input_device *device);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/seat.h",
    "content": "#ifndef _SWAY_INPUT_SEAT_H\n#define _SWAY_INPUT_SEAT_H\n\n#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>\n#include <wlr/types/wlr_layer_shell_v1.h>\n#include <wlr/types/wlr_scene.h>\n#include <wlr/types/wlr_seat.h>\n#include <wlr/types/wlr_touch.h>\n#include <wlr/util/edges.h>\n#include \"sway/config.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/tablet.h\"\n#include \"sway/input/text_input.h\"\n\nstruct sway_seat;\n\nstruct sway_seatop_impl {\n\tvoid (*button)(struct sway_seat *seat, uint32_t time_msec,\n\t\t\tstruct wlr_input_device *device, uint32_t button,\n\t\t\tenum wl_pointer_button_state state);\n\tvoid (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec);\n\tvoid (*pointer_axis)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_axis_event *event);\n\tvoid (*hold_begin)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_hold_begin_event *event);\n\tvoid (*hold_end)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_hold_end_event *event);\n\tvoid (*pinch_begin)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_pinch_begin_event *event);\n\tvoid (*pinch_update)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_pinch_update_event *event);\n\tvoid (*pinch_end)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_pinch_end_event *event);\n\tvoid (*swipe_begin)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_swipe_begin_event *event);\n\tvoid (*swipe_update)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_swipe_update_event *event);\n\tvoid (*swipe_end)(struct sway_seat *seat,\n\t\t\tstruct wlr_pointer_swipe_end_event *event);\n\tvoid (*rebase)(struct sway_seat *seat, uint32_t time_msec);\n\tvoid (*touch_motion)(struct sway_seat *seat,\n\t\t\tstruct wlr_touch_motion_event *event, double lx, double ly);\n\tvoid (*touch_up)(struct sway_seat *seat,\n\t\t\tstruct wlr_touch_up_event *event);\n\tvoid (*touch_down)(struct sway_seat *seat,\n\t\t\tstruct wlr_touch_down_event *event, double lx, double ly);\n\tvoid (*touch_cancel)(struct sway_seat *seat,\n\t\t\tstruct wlr_touch_cancel_event *event);\n\tvoid (*tablet_tool_motion)(struct sway_seat *seat,\n\t\t\tstruct sway_tablet_tool *tool, uint32_t time_msec);\n\tvoid (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool,\n\t\t\tuint32_t time_msec, enum wlr_tablet_tool_tip_state state);\n\tvoid (*end)(struct sway_seat *seat);\n\tvoid (*unref)(struct sway_seat *seat, struct sway_container *con);\n\tbool allow_set_cursor;\n};\n\nstruct sway_seat_device {\n\tstruct sway_seat *sway_seat;\n\tstruct sway_input_device *input_device;\n\tstruct sway_keyboard *keyboard;\n\tstruct sway_switch *switch_device;\n\tstruct sway_tablet *tablet;\n\tstruct sway_tablet_pad *tablet_pad;\n\tstruct wl_list link; // sway_seat::devices\n};\n\nstruct sway_seat_node {\n\tstruct sway_seat *seat;\n\tstruct sway_node *node;\n\n\tstruct wl_list link; // sway_seat::focus_stack\n\n\tstruct wl_listener destroy;\n};\n\nstruct sway_drag {\n\tstruct sway_seat *seat;\n\tstruct wlr_drag *wlr_drag;\n\tstruct wl_listener destroy;\n};\n\nstruct sway_seat {\n\tstruct wlr_seat *wlr_seat;\n\tstruct sway_cursor *cursor;\n\n\t// Seat scene tree structure\n\t// - scene_tree\n\t//   - drag icons\n\t//     - drag icon 1\n\t//     - drag icon 2\n\t//   - seatop specific stuff\n\tstruct wlr_scene_tree *scene_tree;\n\tstruct wlr_scene_tree *drag_icons;\n\n\tbool has_focus;\n\tstruct wl_list focus_stack; // list of containers in focus order\n\tstruct sway_workspace *workspace;\n\tchar *prev_workspace_name; // for workspace back_and_forth\n\n\tstruct wlr_layer_surface_v1 *focused_layer;\n\t// If the exclusive layer is set, views cannot receive keyboard focus\n\tbool has_exclusive_layer;\n\n\t// Last touch point\n\tint32_t touch_id;\n\tdouble touch_x, touch_y;\n\n\t// Seat operations (drag and resize)\n\tconst struct sway_seatop_impl *seatop_impl;\n\tvoid *seatop_data;\n\n\tuint32_t last_button_serial;\n\n\tuint32_t idle_inhibit_sources, idle_wake_sources;\n\n\tlist_t *deferred_bindings; // struct sway_binding\n\n\tstruct sway_input_method_relay im_relay;\n\n\tstruct wl_listener focus_destroy;\n\tstruct wl_listener new_node;\n\tstruct wl_listener request_start_drag;\n\tstruct wl_listener start_drag;\n\tstruct wl_listener request_set_selection;\n\tstruct wl_listener request_set_primary_selection;\n\tstruct wl_listener destroy;\n\n\tstruct wl_list devices; // sway_seat_device::link\n\tstruct wl_list keyboard_groups; // sway_keyboard_group::link\n\tstruct wl_list keyboard_shortcuts_inhibitors;\n\t\t\t\t// sway_keyboard_shortcuts_inhibitor::link\n\n\tstruct wl_list link; // input_manager::seats\n};\n\nstruct sway_pointer_constraint {\n\tstruct sway_cursor *cursor;\n\tstruct wlr_pointer_constraint_v1 *constraint;\n\n\tstruct wl_listener set_region;\n\tstruct wl_listener destroy;\n};\n\nstruct sway_keyboard_shortcuts_inhibitor {\n\tstruct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor;\n\n\tstruct wl_listener destroy;\n\n\tstruct wl_list link; // sway_seat::keyboard_shortcuts_inhibitors\n};\n\nstruct sway_seat *seat_create(const char *seat_name);\n\nvoid seat_destroy(struct sway_seat *seat);\n\nvoid seat_add_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *device);\n\nvoid seat_configure_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *device);\n\nvoid seat_configure_device_mapping(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device);\n\nvoid seat_reset_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device);\n\nvoid seat_remove_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *device);\n\nvoid seat_configure_xcursor(struct sway_seat *seat);\n\nvoid seat_set_focus(struct sway_seat *seat, struct sway_node *node);\n\nvoid seat_set_focus_container(struct sway_seat *seat,\n\t\tstruct sway_container *con);\n\nvoid seat_set_focus_workspace(struct sway_seat *seat,\n\t\tstruct sway_workspace *ws);\n\n/**\n * Manipulate the focus stack without triggering any other behaviour.\n *\n * This can be used to set focus_inactive by calling the function a second time\n * with the real focus.\n */\nvoid seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node);\n\nvoid seat_set_focus_surface(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface, bool unfocus);\n\nvoid seat_set_focus_layer(struct sway_seat *seat,\n\t\tstruct wlr_layer_surface_v1 *layer);\n\nvoid seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client);\n\nstruct sway_node *seat_get_focus(struct sway_seat *seat);\n\nstruct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat);\n\n// If a scratchpad container is fullscreen global, this can be used to try to\n// determine the last focused workspace. Otherwise, this should yield the same\n// results as seat_get_focused_workspace.\nstruct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat);\n\nstruct sway_container *seat_get_focused_container(struct sway_seat *seat);\n\n/**\n * Return the last container to be focused for the seat (or the most recently\n * opened if no container has received focused) that is a child of the given\n * container. The focus-inactive container of the root window is the focused\n * container for the seat (if the seat does have focus). This function can be\n * used to determine what container gets focused next if the focused container\n * is destroyed, or focus moves to a container with children and we need to\n * descend into the next leaf in focus order.\n */\nstruct sway_node *seat_get_focus_inactive(struct sway_seat *seat,\n\t\tstruct sway_node *node);\n\nstruct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,\n\t\tstruct sway_workspace *workspace);\n\n/**\n * Descend into the focus stack to find the focus-inactive view. Useful for\n * container placement when they change position in the tree.\n */\nstruct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,\n\t\tstruct sway_node *ancestor);\n\n/**\n * Return the immediate child of container which was most recently focused.\n */\nstruct sway_node *seat_get_active_tiling_child(struct sway_seat *seat,\n\t\tstruct sway_node *parent);\n\n/**\n * Iterate over the focus-inactive children of the container calling the\n * function on each.\n */\nvoid seat_for_each_node(struct sway_seat *seat,\n\t\tvoid (*f)(struct sway_node *node, void *data), void *data);\n\nvoid seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);\n\nstruct seat_config *seat_get_config(struct sway_seat *seat);\n\nstruct seat_config *seat_get_config_by_name(const char *name);\n\nvoid seat_idle_notify_activity(struct sway_seat *seat,\n\t\tenum sway_input_idle_source source);\n\nbool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);\n\nvoid drag_icons_update_position(struct sway_seat *seat);\n\nenum wlr_edges find_resize_edge(struct sway_container *cont,\n\t\tstruct wlr_surface *surface, struct sway_cursor *cursor);\n\nvoid seatop_begin_default(struct sway_seat *seat);\n\nvoid seatop_begin_down(struct sway_seat *seat, struct sway_container *con,\n\t\tdouble sx, double sy);\n\nvoid seatop_begin_down_on_surface(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface, double sx, double sy);\n\nvoid seatop_begin_touch_down(struct sway_seat *seat, struct wlr_surface *surface,\n\t\tstruct wlr_touch_down_event *event, double sx, double sy, double lx, double ly);\n\nvoid seatop_begin_move_floating(struct sway_seat *seat,\n\t\tstruct sway_container *con);\n\nvoid seatop_begin_move_tiling_threshold(struct sway_seat *seat,\n\t\tstruct sway_container *con);\n\nvoid seatop_begin_move_tiling(struct sway_seat *seat,\n\t\tstruct sway_container *con);\n\nvoid seatop_begin_resize_floating(struct sway_seat *seat,\n\t\tstruct sway_container *con, enum wlr_edges edge);\n\nvoid seatop_begin_resize_tiling(struct sway_seat *seat,\n\t\tstruct sway_container *con, enum wlr_edges edge);\n\nstruct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,\n\t\tstruct sway_workspace *workspace);\n\nvoid seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tuint32_t button, enum wl_pointer_button_state state);\n\nvoid seat_consider_warp_to_focus(struct sway_seat *seat);\n\nvoid seatop_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state);\n\nvoid seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);\n\nvoid seatop_pointer_axis(struct sway_seat *seat,\n\t\tstruct wlr_pointer_axis_event *event);\n\nvoid seatop_tablet_tool_tip(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec,\n\t\tenum wlr_tablet_tool_tip_state state);\n\nvoid seatop_tablet_tool_motion(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec);\n\nvoid seatop_hold_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_hold_begin_event *event);\nvoid seatop_hold_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_hold_end_event *event);\n\nvoid seatop_pinch_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_begin_event *event);\nvoid seatop_pinch_update(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_update_event *event);\nvoid seatop_pinch_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_end_event *event);\n\nvoid seatop_swipe_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_begin_event *event);\nvoid seatop_swipe_update(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_update_event *event);\nvoid seatop_swipe_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_end_event *event);\n\nvoid seatop_touch_motion(struct sway_seat *seat,\n\t\tstruct wlr_touch_motion_event *event, double lx, double ly);\n\nvoid seatop_touch_up(struct sway_seat *seat,\n\t\tstruct wlr_touch_up_event *event);\n\nvoid seatop_touch_down(struct sway_seat *seat,\n\t\tstruct wlr_touch_down_event *event, double lx, double ly);\n\nvoid seatop_touch_cancel(struct sway_seat *seat,\n\t\tstruct wlr_touch_cancel_event *event);\n\nvoid seatop_rebase(struct sway_seat *seat, uint32_t time_msec);\n\n/**\n * End a seatop (ie. free any seatop specific resources).\n */\nvoid seatop_end(struct sway_seat *seat);\n\n/**\n * Instructs the seatop implementation to drop any references to the given\n * container (eg. because the container is destroying).\n * The seatop may choose to abort itself in response to this.\n */\nvoid seatop_unref(struct sway_seat *seat, struct sway_container *con);\n\nbool seatop_allows_set_cursor(struct sway_seat *seat);\n\n/**\n * Returns the keyboard shortcuts inhibitor that applies to the given surface\n * or NULL if none exists.\n */\nstruct sway_keyboard_shortcuts_inhibitor *\nkeyboard_shortcuts_inhibitor_get_for_surface(const struct sway_seat *seat,\n\t\tconst struct wlr_surface *surface);\n\n/**\n * Returns the keyboard shortcuts inhibitor that applies to the currently\n * focused surface of a seat or NULL if none exists.\n */\nstruct sway_keyboard_shortcuts_inhibitor *\nkeyboard_shortcuts_inhibitor_get_for_focused_surface(const struct sway_seat *seat);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/switch.h",
    "content": "#ifndef _SWAY_INPUT_SWITCH_H\n#define _SWAY_INPUT_SWITCH_H\n\n#include \"sway/input/seat.h\"\n\nstruct sway_switch {\n\tstruct sway_seat_device *seat_device;\n\tstruct wlr_switch *wlr;\n\tenum wlr_switch_state state;\n\tenum wlr_switch_type type;\n\n\tstruct wl_listener switch_toggle;\n};\n\nstruct sway_switch *sway_switch_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device);\n\nvoid sway_switch_configure(struct sway_switch *sway_switch);\n\nvoid sway_switch_destroy(struct sway_switch *sway_switch);\n\nvoid sway_switch_retrigger_bindings_for_all(void);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/tablet.h",
    "content": "#ifndef _SWAY_INPUT_TABLET_H\n#define _SWAY_INPUT_TABLET_H\n#include <wlr/types/wlr_layer_shell_v1.h>\n\nstruct sway_seat;\nstruct wlr_tablet_tool;\n\nstruct sway_tablet {\n\tstruct wl_list link;\n\tstruct sway_seat_device *seat_device;\n\tstruct wlr_tablet_v2_tablet *tablet_v2;\n};\n\nenum sway_tablet_tool_mode {\n\tSWAY_TABLET_TOOL_MODE_ABSOLUTE,\n\tSWAY_TABLET_TOOL_MODE_RELATIVE,\n};\n\nstruct sway_tablet_tool {\n\tstruct sway_seat *seat;\n\tstruct sway_tablet *tablet;\n\tstruct wlr_tablet_v2_tablet_tool *tablet_v2_tool;\n\n\tenum sway_tablet_tool_mode mode;\n\tdouble tilt_x, tilt_y;\n\n\tstruct wl_listener set_cursor;\n\tstruct wl_listener tool_destroy;\n};\n\nstruct sway_tablet_pad {\n\tstruct wl_list link;\n\tstruct sway_seat_device *seat_device;\n\tstruct sway_tablet *tablet;\n\tstruct wlr_tablet_pad *wlr;\n\tstruct wlr_tablet_v2_tablet_pad *tablet_v2_pad;\n\n\tstruct wl_listener attach;\n\tstruct wl_listener button;\n\tstruct wl_listener ring;\n\tstruct wl_listener strip;\n\n\tstruct wlr_surface *current_surface;\n\tstruct wl_listener surface_destroy;\n\n\tstruct wl_listener tablet_destroy;\n};\n\nstruct sway_tablet *sway_tablet_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device);\n\nvoid sway_configure_tablet(struct sway_tablet *tablet);\n\nvoid sway_tablet_destroy(struct sway_tablet *tablet);\n\nvoid sway_tablet_tool_configure(struct sway_tablet *tablet,\n\t\tstruct wlr_tablet_tool *wlr_tool);\n\nstruct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device);\n\nvoid sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad);\n\nvoid sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad);\n\nvoid sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,\n\t\tstruct wlr_surface *surface);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/text_input.h",
    "content": "#ifndef _SWAY_INPUT_TEXT_INPUT_H\n#define _SWAY_INPUT_TEXT_INPUT_H\n\n#include <wlr/types/wlr_text_input_v3.h>\n#include <wlr/types/wlr_input_method_v2.h>\n#include <wlr/types/wlr_compositor.h>\n\n/**\n * The relay structure manages the relationship between text-input and\n * input_method interfaces on a given seat. Multiple text-input interfaces may\n * be bound to a relay, but at most one will be focused (receiving events) at\n * a time. At most one input-method interface may be bound to the seat. The\n * relay manages life cycle of both sides. When both sides are present and\n * focused, the relay passes messages between them.\n *\n * Text input focus is a subset of keyboard focus - if the text-input is\n * in the focused state, wl_keyboard sent an enter as well. However, having\n * wl_keyboard focused doesn't mean that text-input will be focused.\n */\nstruct sway_input_method_relay {\n\tstruct sway_seat *seat;\n\n\tstruct wl_list text_inputs; // sway_text_input::link\n\tstruct wl_list input_popups; // sway_input_popup::link\n\tstruct wlr_input_method_v2 *input_method; // doesn't have to be present\n\n\tstruct wl_listener text_input_new;\n\tstruct wl_listener text_input_manager_destroy;\n\n\tstruct wl_listener input_method_new;\n\tstruct wl_listener input_method_manager_destroy;\n\tstruct wl_listener input_method_commit;\n\tstruct wl_listener input_method_new_popup_surface;\n\tstruct wl_listener input_method_grab_keyboard;\n\tstruct wl_listener input_method_destroy;\n\n\tstruct wl_listener input_method_keyboard_grab_destroy;\n};\n\n\nstruct sway_text_input {\n\tstruct sway_input_method_relay *relay;\n\n\tstruct wlr_text_input_v3 *input;\n\t// The surface getting seat's focus. Stored for when text-input cannot\n\t// be sent an enter event immediately after getting focus, e.g. when\n\t// there's no input method available. Cleared once text-input is entered.\n\tstruct wlr_surface *pending_focused_surface;\n\n\tstruct wl_list link;\n\n\tstruct wl_listener pending_focused_surface_destroy;\n\n\tstruct wl_listener text_input_enable;\n\tstruct wl_listener text_input_commit;\n\tstruct wl_listener text_input_disable;\n\tstruct wl_listener text_input_destroy;\n};\n\nvoid sway_input_method_relay_init(struct sway_seat *seat,\n\tstruct sway_input_method_relay *relay);\n\nvoid sway_input_method_relay_finish(struct sway_input_method_relay *relay);\n\n// Updates currently focused surface. Surface must belong to the same seat.\nvoid sway_input_method_relay_set_focus(struct sway_input_method_relay *relay,\n\tstruct wlr_surface *surface);\n\nstruct sway_text_input *sway_text_input_create(\n\tstruct sway_input_method_relay *relay,\n\tstruct wlr_text_input_v3 *text_input);\n\n#endif\n"
  },
  {
    "path": "include/sway/input/text_input_popup.h",
    "content": "#ifndef _SWAY_INPUT_TEXT_INPUT_POPUP_H\n#define _SWAY_INPUT_TEXT_INPUT_POPUP_H\n\n#include \"sway/tree/view.h\"\n\nstruct sway_input_popup {\n\tstruct sway_input_method_relay *relay;\n\n\tstruct wlr_scene_tree *scene_tree;\n\tstruct sway_popup_desc desc;\n\tstruct wlr_input_popup_surface_v2 *popup_surface;\n\tstruct wlr_output *fixed_output;\n\n\tstruct wl_list link;\n\n\tstruct wl_listener popup_destroy;\n\tstruct wl_listener popup_surface_commit;\n\tstruct wl_listener popup_surface_map;\n\tstruct wl_listener popup_surface_unmap;\n\n\tstruct wl_listener focused_surface_unmap;\n};\n#endif\n"
  },
  {
    "path": "include/sway/ipc-json.h",
    "content": "#ifndef _SWAY_IPC_JSON_H\n#define _SWAY_IPC_JSON_H\n#include <json.h>\n#include \"sway/output.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/input/input-manager.h\"\n\njson_object *ipc_json_get_version(void);\n\njson_object *ipc_json_get_binding_mode(void);\n\njson_object *ipc_json_describe_disabled_output(struct sway_output *o);\njson_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *o);\njson_object *ipc_json_describe_node(struct sway_node *node);\njson_object *ipc_json_describe_node_recursive(struct sway_node *node);\njson_object *ipc_json_describe_input(struct sway_input_device *device);\njson_object *ipc_json_describe_seat(struct sway_seat *seat);\njson_object *ipc_json_describe_bar_config(struct bar_config *bar);\n\n#endif\n"
  },
  {
    "path": "include/sway/ipc-server.h",
    "content": "#ifndef _SWAY_IPC_SERVER_H\n#define _SWAY_IPC_SERVER_H\n#include <sys/socket.h>\n#include \"sway/config.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/tree/container.h\"\n#include \"ipc.h\"\n\nstruct sway_server;\n\nvoid ipc_init(struct sway_server *server);\n\nstruct sockaddr_un *ipc_user_sockaddr(void);\n\nvoid ipc_event_workspace(struct sway_workspace *old,\n\t\tstruct sway_workspace *new, const char *change);\nvoid ipc_event_window(struct sway_container *window, const char *change);\nvoid ipc_event_barconfig_update(struct bar_config *bar);\nvoid ipc_event_bar_state_update(struct bar_config *bar);\nvoid ipc_event_mode(const char *mode, bool pango);\nvoid ipc_event_shutdown(const char *reason);\nvoid ipc_event_binding(struct sway_binding *binding);\nvoid ipc_event_input(const char *change, struct sway_input_device *device);\nvoid ipc_event_output(void);\n\n#endif\n"
  },
  {
    "path": "include/sway/layers.h",
    "content": "#ifndef _SWAY_LAYERS_H\n#define _SWAY_LAYERS_H\n#include <stdbool.h>\n#include <wlr/types/wlr_compositor.h>\n#include <wlr/types/wlr_layer_shell_v1.h>\n#include \"sway/tree/view.h\"\n\nstruct sway_layer_surface {\n\tstruct wl_listener map;\n\tstruct wl_listener unmap;\n\tstruct wl_listener surface_commit;\n\tstruct wl_listener node_destroy;\n\tstruct wl_listener new_popup;\n\n\tbool mapped;\n\n\tstruct wlr_scene_tree *popups;\n\tstruct sway_popup_desc desc;\n\n\tstruct sway_output *output;\n\tstruct wl_list link; // sway_output.layer_surfaces\n\n\tstruct wlr_scene_layer_surface_v1 *scene;\n\tstruct wlr_scene_tree *tree;\n\tstruct wlr_layer_surface_v1 *layer_surface;\n};\n\nstruct sway_layer_popup {\n\tstruct wlr_xdg_popup *wlr_popup;\n\tstruct wlr_scene_tree *scene;\n\tstruct sway_layer_surface *toplevel;\n\n\tstruct wl_listener destroy;\n\tstruct wl_listener new_popup;\n\tstruct wl_listener commit;\n\tstruct wl_listener reposition;\n};\n\nstruct sway_output;\n\nstruct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(\n\t\tstruct wlr_surface *surface);\n\nvoid arrange_layers(struct sway_output *output);\n\nvoid destroy_layers(struct sway_output *output);\n\n#endif\n"
  },
  {
    "path": "include/sway/lock.h",
    "content": "#ifndef _SWAY_LOCK_H\n#define _SWAY_LOCK_H\n\nvoid arrange_locks(void);\n\n#endif\n"
  },
  {
    "path": "include/sway/output.h",
    "content": "#ifndef _SWAY_OUTPUT_H\n#define _SWAY_OUTPUT_H\n#include <time.h>\n#include <unistd.h>\n#include <wayland-server-core.h>\n#include <wlr/types/wlr_damage_ring.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_scene.h>\n#include \"config.h\"\n#include \"sway/tree/node.h\"\n#include \"sway/tree/view.h\"\n\nstruct sway_server;\nstruct sway_container;\n\nstruct sway_output_state {\n\tlist_t *workspaces;\n\tstruct sway_workspace *active_workspace;\n};\n\nstruct sway_output {\n\tstruct sway_node node;\n\n\tstruct {\n\t\tstruct wlr_scene_tree *shell_background;\n\t\tstruct wlr_scene_tree *shell_bottom;\n\t\tstruct wlr_scene_tree *tiling;\n\t\tstruct wlr_scene_tree *fullscreen;\n\t\tstruct wlr_scene_tree *shell_top;\n\t\tstruct wlr_scene_tree *shell_overlay;\n\t\tstruct wlr_scene_tree *session_lock;\n\t} layers;\n\n\t// when a container is fullscreen, in case the fullscreen surface is\n\t// translucent (can see behind) we must make sure that the background is a\n\t// solid color in order to conform to the wayland protocol. This rect\n\t// ensures that when looking through a surface, all that will be seen\n\t// is black.\n\tstruct wlr_scene_rect *fullscreen_background;\n\n\tstruct wlr_output *wlr_output;\n\tstruct wlr_scene_output *scene_output;\n\tstruct sway_server *server;\n\tstruct wl_list link;\n\n\tstruct wlr_box usable_area;\n\n\tint lx, ly; // layout coords\n\tint width, height; // transformed buffer size\n\tenum wl_output_subpixel detected_subpixel;\n\tenum scale_filter_mode scale_filter;\n\n\tbool enabled;\n\tlist_t *workspaces;\n\tstruct wl_list layer_surfaces; // sway_layer_surface.link\n\n\tstruct sway_output_state current;\n\n\tstruct wl_listener layout_destroy;\n\tstruct wl_listener destroy;\n\tstruct wl_listener present;\n\tstruct wl_listener frame;\n\tstruct wl_listener request_state;\n\n\tstruct wlr_color_transform *color_transform;\n\n\tstruct timespec last_presentation;\n\tuint32_t refresh_nsec;\n\tint max_render_time; // In milliseconds\n\tstruct wl_event_source *repaint_timer;\n\n\tbool allow_tearing;\n\tbool hdr;\n};\n\nstruct sway_output_non_desktop {\n\tstruct wlr_output *wlr_output;\n\n\tstruct wl_listener destroy;\n};\n\nstruct sway_output *output_create(struct wlr_output *wlr_output);\n\nvoid output_destroy(struct sway_output *output);\n\nvoid output_begin_destroy(struct sway_output *output);\n\nstruct sway_output *output_from_wlr_output(struct wlr_output *output);\n\nstruct sway_output *output_get_in_direction(struct sway_output *reference,\n\t\tenum wlr_direction direction);\n\nvoid output_configure_scene(struct sway_output *output,\n\tstruct wlr_scene_node *node, float opacity);\n\nvoid output_add_workspace(struct sway_output *output,\n\t\tstruct sway_workspace *workspace);\n\ntypedef void (*sway_surface_iterator_func_t)(struct sway_output *output,\n\tstruct sway_view *view, struct wlr_surface *surface, struct wlr_box *box,\n\tvoid *user_data);\n\nbool output_match_name_or_id(struct sway_output *output,\n\tconst char *name_or_id);\n\n// this ONLY includes the enabled outputs\nstruct sway_output *output_by_name_or_id(const char *name_or_id);\n\n// this includes all the outputs, including disabled ones\nstruct sway_output *all_output_by_name_or_id(const char *name_or_id);\n\nvoid output_sort_workspaces(struct sway_output *output);\n\nvoid output_enable(struct sway_output *output);\n\nvoid output_disable(struct sway_output *output);\n\nstruct sway_workspace *output_get_active_workspace(struct sway_output *output);\n\nvoid output_for_each_workspace(struct sway_output *output,\n\t\tvoid (*f)(struct sway_workspace *ws, void *data), void *data);\n\nvoid output_for_each_container(struct sway_output *output,\n\t\tvoid (*f)(struct sway_container *con, void *data), void *data);\n\nstruct sway_workspace *output_find_workspace(struct sway_output *output,\n\t\tbool (*test)(struct sway_workspace *ws, void *data), void *data);\n\nstruct sway_container *output_find_container(struct sway_output *output,\n\t\tbool (*test)(struct sway_container *con, void *data), void *data);\n\nvoid output_get_box(struct sway_output *output, struct wlr_box *box);\n\nbool output_supports_hdr(struct wlr_output *output, const char **unsupported_reason_ptr);\n\nenum sway_container_layout output_get_default_layout(\n\t\tstruct sway_output *output);\n\nenum wlr_direction opposite_direction(enum wlr_direction d);\n\nvoid handle_output_manager_apply(struct wl_listener *listener, void *data);\n\nvoid handle_output_manager_test(struct wl_listener *listener, void *data);\n\nvoid handle_output_power_manager_set_mode(struct wl_listener *listener,\n\tvoid *data);\n\nstruct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);\n\nvoid update_output_manager_config(struct sway_server *server);\n\n#endif\n"
  },
  {
    "path": "include/sway/scene_descriptor.h",
    "content": "/**\n * Across a wayland compositor, there are multiple shells: It can be\n * a toplevel, or a layer_shell, or even something more meta like a drag\n * icon or highlight indicators when dragging windows around.\n *\n * This object lets us store values that represent these modes of operation\n * and keep track of what object is being represented.\n */\n#ifndef _SWAY_SCENE_DESCRIPTOR_H\n#define _SWAY_SCENE_DESCRIPTOR_H\n#include <wlr/types/wlr_scene.h>\n\nenum sway_scene_descriptor_type {\n\tSWAY_SCENE_DESC_BUFFER_TIMER,\n\tSWAY_SCENE_DESC_NON_INTERACTIVE,\n\tSWAY_SCENE_DESC_CONTAINER,\n\tSWAY_SCENE_DESC_VIEW,\n\tSWAY_SCENE_DESC_LAYER_SHELL,\n\tSWAY_SCENE_DESC_XWAYLAND_UNMANAGED,\n\tSWAY_SCENE_DESC_POPUP,\n\tSWAY_SCENE_DESC_DRAG_ICON,\n};\n\nbool scene_descriptor_assign(struct wlr_scene_node *node,\n\tenum sway_scene_descriptor_type type, void *data);\n\nvoid *scene_descriptor_try_get(struct wlr_scene_node *node,\n\tenum sway_scene_descriptor_type type);\n\nvoid scene_descriptor_destroy(struct wlr_scene_node *node,\n\tenum sway_scene_descriptor_type type);\n\n#endif\n"
  },
  {
    "path": "include/sway/server.h",
    "content": "#ifndef _SWAY_SERVER_H\n#define _SWAY_SERVER_H\n#include <stdbool.h>\n#include <wayland-server-core.h>\n#include \"config.h\"\n#include \"list.h\"\n#include \"sway/desktop/idle_inhibit_v1.h\"\n#if WLR_HAS_XWAYLAND\n#include \"sway/xwayland.h\"\n#endif\n\nstruct sway_transaction;\n\nstruct sway_session_lock {\n\tstruct wlr_session_lock_v1 *lock;\n\tstruct wlr_surface *focused;\n\tbool abandoned;\n\n\tstruct wl_list outputs; // struct sway_session_lock_output\n\n\t// invalid if the session is abandoned\n\tstruct wl_listener new_surface;\n\tstruct wl_listener unlock;\n\tstruct wl_listener destroy;\n};\n\nstruct sway_server {\n\tstruct wl_display *wl_display;\n\tstruct wl_event_loop *wl_event_loop;\n\tchar *socket;\n\n\tstruct wlr_backend *backend;\n\tstruct wlr_session *session;\n\t// secondary headless backend used for creating virtual outputs on-the-fly\n\tstruct wlr_backend *headless_backend;\n\tstruct wlr_renderer *renderer;\n\tstruct wlr_allocator *allocator;\n\n\tstruct wlr_compositor *compositor;\n\n\tstruct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;\n\n\tstruct wlr_data_device_manager *data_device_manager;\n\n\tstruct sway_input_manager *input;\n\n\tstruct wl_listener new_output;\n\tstruct wl_listener renderer_lost;\n\tstruct wl_event_source *recreating_renderer;\n\n\tstruct wlr_idle_notifier_v1 *idle_notifier_v1;\n\tstruct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1;\n\n\tstruct wlr_layer_shell_v1 *layer_shell;\n\tstruct wl_listener layer_shell_surface;\n\n\tstruct wlr_xdg_shell *xdg_shell;\n\tstruct wl_listener xdg_shell_toplevel;\n\n\tstruct wlr_tablet_manager_v2 *tablet_v2;\n\n#if WLR_HAS_XWAYLAND\n\tstruct sway_xwayland xwayland;\n\tstruct wl_listener xwayland_surface;\n\tstruct wl_listener xwayland_ready;\n#endif\n\n\tstruct wlr_relative_pointer_manager_v1 *relative_pointer_manager;\n\n\tstruct wlr_server_decoration_manager *server_decoration_manager;\n\tstruct wl_listener server_decoration;\n\tstruct wl_list decorations; // sway_server_decoration::link\n\n\tstruct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager;\n\tstruct wl_listener xdg_decoration;\n\tstruct wl_list xdg_decorations; // sway_xdg_decoration::link\n\n\tstruct wlr_drm_lease_v1_manager *drm_lease_manager;\n\tstruct wl_listener drm_lease_request;\n\n\tstruct wlr_pointer_constraints_v1 *pointer_constraints;\n\tstruct wl_listener pointer_constraint;\n\n\tstruct wlr_xdg_output_manager_v1 *xdg_output_manager_v1;\n\n\tstruct wlr_output_manager_v1 *output_manager_v1;\n\tstruct wl_listener output_manager_apply;\n\tstruct wl_listener output_manager_test;\n\n\tstruct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;\n\tstruct wl_listener gamma_control_set_gamma;\n\n\tstruct {\n\t\tstruct sway_session_lock *lock;\n\t\tstruct wlr_session_lock_manager_v1 *manager;\n\n\t\tstruct wl_listener new_lock;\n\t\tstruct wl_listener manager_destroy;\n\t} session_lock;\n\n\tstruct wlr_output_power_manager_v1 *output_power_manager_v1;\n\tstruct wl_listener output_power_manager_set_mode;\n\tstruct wlr_input_method_manager_v2 *input_method;\n\tstruct wlr_text_input_manager_v3 *text_input;\n\tstruct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;\n\tstruct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;\n\tstruct wlr_content_type_manager_v1 *content_type_manager_v1;\n\tstruct wlr_data_control_manager_v1 *wlr_data_control_manager_v1;\n\tstruct wlr_ext_data_control_manager_v1 *ext_data_control_manager_v1;\n\tstruct wlr_screencopy_manager_v1 *screencopy_manager_v1;\n\tstruct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_manager_v1;\n\tstruct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1;\n\tstruct wlr_security_context_manager_v1 *security_context_manager_v1;\n\n\tstruct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_foreign_toplevel_image_capture_source_manager_v1;\n\tstruct wl_listener new_foreign_toplevel_capture_request;\n\n\tstruct wlr_xdg_activation_v1 *xdg_activation_v1;\n\tstruct wl_listener xdg_activation_v1_request_activate;\n\tstruct wl_listener xdg_activation_v1_new_token;\n\n\tstruct wl_listener xdg_toplevel_tag_manager_v1_set_tag;\n\n\tstruct wl_listener request_set_cursor_shape;\n\n\tstruct wlr_tearing_control_manager_v1 *tearing_control_v1;\n\tstruct wl_listener tearing_control_new_object;\n\tstruct wl_list tearing_controllers; // sway_tearing_controller::link\n\n\tstruct wl_list pending_launcher_ctxs; // launcher_ctx::link\n\n\t// The timeout for transactions, after which a transaction is applied\n\t// regardless of readiness.\n\tsize_t txn_timeout_ms;\n\n\t// Stores a transaction after it has been committed, but is waiting for\n\t// views to ack the new dimensions before being applied. A queued\n\t// transaction is frozen and must not have new instructions added to it.\n\tstruct sway_transaction *queued_transaction;\n\n\t// Stores a pending transaction that will be committed once the existing\n\t// queued transaction is applied and freed. The pending transaction can be\n\t// updated with new instructions as needed.\n\tstruct sway_transaction *pending_transaction;\n\n\t// Stores the nodes that have been marked as \"dirty\" and will be put into\n\t// the pending transaction.\n\tlist_t *dirty_nodes;\n\n\tstruct wl_event_source *delayed_modeset;\n};\n\nextern struct sway_server server;\n\nstruct sway_debug {\n\tbool noatomic;         // Ignore atomic layout updates\n\tbool txn_timings;      // Log verbose messages about transactions\n\tbool txn_wait;         // Always wait for the timeout before applying\n};\n\nextern struct sway_debug debug;\n\nextern bool unsupported_gpu_detected;\n\nvoid sway_terminate(int exit_code);\n\nbool server_init(struct sway_server *server);\nvoid server_fini(struct sway_server *server);\nbool server_start(struct sway_server *server);\nvoid server_run(struct sway_server *server);\n\nvoid handle_new_output(struct wl_listener *listener, void *data);\n\nvoid handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);\nvoid handle_layer_shell_surface(struct wl_listener *listener, void *data);\nvoid sway_session_lock_init(void);\nvoid sway_session_lock_add_output(struct sway_session_lock *lock,\n\tstruct sway_output *output);\nbool sway_session_lock_has_surface(struct sway_session_lock *lock,\n\tstruct wlr_surface *surface);\nvoid handle_xdg_shell_toplevel(struct wl_listener *listener, void *data);\n#if WLR_HAS_XWAYLAND\nvoid handle_xwayland_surface(struct wl_listener *listener, void *data);\n#endif\nvoid handle_server_decoration(struct wl_listener *listener, void *data);\nvoid handle_xdg_decoration(struct wl_listener *listener, void *data);\nvoid handle_pointer_constraint(struct wl_listener *listener, void *data);\nvoid xdg_activation_v1_handle_request_activate(struct wl_listener *listener,\n\tvoid *data);\nvoid xdg_activation_v1_handle_new_token(struct wl_listener *listener,\n\tvoid *data);\n\nvoid set_rr_scheduling(void);\n\nvoid handle_new_tearing_hint(struct wl_listener *listener, void *data);\n\n#endif\n"
  },
  {
    "path": "include/sway/sway_text_node.h",
    "content": "#ifndef _SWAY_BUFFER_H\n#define _SWAY_BUFFER_H\n#include <wlr/types/wlr_scene.h>\n\nstruct sway_text_node {\n\tint width;\n\tint max_width;\n\tint height;\n\tint baseline;\n\tbool pango_markup;\n\tfloat color[4];\n\tfloat background[4];\n\n\tstruct wlr_scene_node *node;\n};\n\nstruct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,\n\t\tchar *text, float color[4], bool pango_markup);\n\nvoid sway_text_node_set_color(struct sway_text_node *node, float color[4]);\n\nvoid sway_text_node_set_text(struct sway_text_node *node, char *text);\n\nvoid sway_text_node_set_max_width(struct sway_text_node *node, int max_width);\n\nvoid sway_text_node_set_background(struct sway_text_node *node, float background[4]);\n\n#endif\n"
  },
  {
    "path": "include/sway/swaynag.h",
    "content": "#ifndef _SWAY_SWAYNAG_H\n#define _SWAY_SWAYNAG_H\n#include <wayland-server-core.h>\n#include \"stringop.h\"\n\nstruct swaynag_instance {\n\tstruct wl_client *client;\n\tstruct wl_listener client_destroy;\n\n\tconst char *args;\n\tint fd[2];\n\tbool detailed;\n};\n\n// Spawn swaynag. If swaynag->detailed, then swaynag->fd[1] will left open\n// so it can be written to. Call swaynag_show when done writing. This will\n// be automatically called by swaynag_log if the instance is not spawned and\n// swaynag->detailed is true.\nbool swaynag_spawn(const char *swaynag_command,\n\t\tstruct swaynag_instance *swaynag);\n\n// Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed\n// is false.\nvoid swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,\n\t\tconst char *fmt, ...) _SWAY_ATTRIB_PRINTF(3, 4);\n\n// If swaynag->detailed, close swaynag->fd[1] so swaynag displays\nvoid swaynag_show(struct swaynag_instance *swaynag);\n\n#endif\n"
  },
  {
    "path": "include/sway/tree/arrange.h",
    "content": "#ifndef _SWAY_ARRANGE_H\n#define _SWAY_ARRANGE_H\n\nstruct sway_output;\nstruct sway_workspace;\nstruct sway_container;\nstruct sway_node;\n\nvoid arrange_container(struct sway_container *container);\n\nvoid arrange_workspace(struct sway_workspace *workspace);\n\nvoid arrange_output(struct sway_output *output);\n\nvoid arrange_root(void);\n\nvoid arrange_node(struct sway_node *node);\n\n#endif\n"
  },
  {
    "path": "include/sway/tree/container.h",
    "content": "#ifndef _SWAY_CONTAINER_H\n#define _SWAY_CONTAINER_H\n#include <stdint.h>\n#include <sys/types.h>\n#include <wlr/types/wlr_compositor.h>\n#include <wlr/types/wlr_scene.h>\n#include \"list.h\"\n#include \"sway/tree/node.h\"\n\nstruct sway_view;\nstruct sway_seat;\n\nenum sway_container_layout {\n\tL_NONE,\n\tL_HORIZ,\n\tL_VERT,\n\tL_STACKED,\n\tL_TABBED,\n};\n\nenum sway_container_border {\n\tB_NONE,\n\tB_PIXEL,\n\tB_NORMAL,\n\tB_CSD,\n};\n\nenum sway_fullscreen_mode {\n\tFULLSCREEN_NONE,\n\tFULLSCREEN_WORKSPACE,\n\tFULLSCREEN_GLOBAL,\n};\n\nstruct sway_root;\nstruct sway_output;\nstruct sway_workspace;\nstruct sway_view;\n\nenum wlr_direction;\n\nstruct sway_container_state {\n\t// Container properties\n\tenum sway_container_layout layout;\n\tdouble x, y;\n\tdouble width, height;\n\n\tenum sway_fullscreen_mode fullscreen_mode;\n\n\tstruct sway_workspace *workspace; // NULL when hidden in the scratchpad\n\tstruct sway_container *parent;    // NULL if container in root of workspace\n\tlist_t *children;                 // struct sway_container\n\n\tstruct sway_container *focused_inactive_child;\n\tbool focused;\n\n\tenum sway_container_border border;\n\tint border_thickness;\n\tbool border_top;\n\tbool border_bottom;\n\tbool border_left;\n\tbool border_right;\n\n\t// These are in layout coordinates.\n\tdouble content_x, content_y;\n\tdouble content_width, content_height;\n};\n\nstruct sway_container {\n\tstruct sway_node node;\n\tstruct sway_view *view;\n\n\tstruct wlr_scene_tree *scene_tree;\n\n\tstruct {\n\t\tstruct wlr_scene_tree *tree;\n\n\t\tstruct wlr_scene_tree *border;\n\t\tstruct wlr_scene_tree *background;\n\n\t\tstruct sway_text_node *title_text;\n\t\tstruct sway_text_node *marks_text;\n\t} title_bar;\n\n\tstruct {\n\t\tstruct wlr_scene_tree *tree;\n\n\t\tstruct wlr_scene_rect *top;\n\t\tstruct wlr_scene_rect *bottom;\n\t\tstruct wlr_scene_rect *left;\n\t\tstruct wlr_scene_rect *right;\n\t} border;\n\n\tstruct wlr_scene_tree *content_tree;\n\tstruct wlr_scene_buffer *output_handler;\n\n\tstruct wl_listener outputs_update;\n\tstruct wl_listener output_handler_destroy;\n\n\tstruct sway_container_state current;\n\tstruct sway_container_state pending;\n\n\tchar *title;           // The view's title (unformatted)\n\tchar *formatted_title; // The title displayed in the title bar\n\tint title_width;\n\n\tchar *title_format;\n\n\tenum sway_container_layout prev_split_layout;\n\n\t// Whether stickiness has been enabled on this container. Use\n\t// `container_is_sticky_[or_child]` rather than accessing this field\n\t// directly; it'll also check that the container is floating.\n\tbool is_sticky;\n\n\t// For C_ROOT, this has no meaning\n\t// For other types, this is the position in layout coordinates\n\t// Includes borders\n\tdouble saved_x, saved_y;\n\tdouble saved_width, saved_height;\n\n\t// Used when the view changes to CSD unexpectedly. This will be a non-B_CSD\n\t// border which we use to restore when the view returns to SSD.\n\tenum sway_container_border saved_border;\n\n\t// The share of the space of parent container this container occupies\n\tdouble width_fraction;\n\tdouble height_fraction;\n\n\t// The share of space of the parent container that all children occupy\n\t// Used for doing the resize calculations\n\tdouble child_total_width;\n\tdouble child_total_height;\n\n\t// Indicates that the container is a scratchpad container.\n\t// Both hidden and visible scratchpad containers have scratchpad=true.\n\t// Hidden scratchpad containers have a NULL parent.\n\tbool scratchpad;\n\n\t// Stores last output size and position for adjusting coordinates of\n\t// scratchpad windows.\n\t// Unused for non-scratchpad windows.\n\tstruct wlr_box transform;\n\n\tfloat alpha;\n\n\tlist_t *marks; // char *\n\n\tstruct {\n\t\tstruct wl_signal destroy;\n\t} events;\n};\n\nstruct sway_container *container_create(struct sway_view *view);\n\nvoid container_destroy(struct sway_container *con);\n\nvoid container_begin_destroy(struct sway_container *con);\n\n/**\n * Search a container's descendants a container based on test criteria. Returns\n * the first container that passes the test.\n */\nstruct sway_container *container_find_child(struct sway_container *container,\n\t\tbool (*test)(struct sway_container *view, void *data), void *data);\n\nvoid container_for_each_child(struct sway_container *container,\n\t\tvoid (*f)(struct sway_container *container, void *data), void *data);\n\n/**\n * Returns the fullscreen container obstructing this container if it exists.\n */\nstruct sway_container *container_obstructing_fullscreen_container(struct sway_container *container);\n\n/**\n * Returns true if the given container is an ancestor of this container.\n */\nbool container_has_ancestor(struct sway_container *container,\n\t\tstruct sway_container *ancestor);\n\nvoid container_reap_empty(struct sway_container *con);\n\nstruct sway_container *container_flatten(struct sway_container *container);\n\nvoid container_update_title_bar(struct sway_container *container);\n\nvoid container_update_marks(struct sway_container *container);\n\nsize_t parse_title_format(struct sway_container *container, char *buffer);\n\nsize_t container_build_representation(enum sway_container_layout layout,\n\t\tlist_t *children, char *buffer);\n\nvoid container_update_representation(struct sway_container *container);\n\n/**\n * Return the height of a regular title bar.\n */\nsize_t container_titlebar_height(void);\n\nvoid floating_calculate_constraints(int *min_width, int *max_width,\n\t\tint *min_height, int *max_height);\n\nvoid floating_fix_coordinates(struct sway_container *con,\n\t\tstruct wlr_box *old, struct wlr_box *new);\n\nvoid container_floating_resize_and_center(struct sway_container *con);\n\nvoid container_floating_set_default_size(struct sway_container *con);\n\nvoid container_set_resizing(struct sway_container *con, bool resizing);\n\nvoid container_set_floating(struct sway_container *container, bool enable);\n\nvoid container_set_geometry_from_content(struct sway_container *con);\n\n/**\n * Determine if the given container is itself floating.\n * This will return false for any descendants of a floating container.\n *\n * Uses pending container state.\n */\nbool container_is_floating(struct sway_container *container);\n\n/**\n * Get a container's box in layout coordinates.\n */\nvoid container_get_box(struct sway_container *container, struct wlr_box *box);\n\n/**\n * Move a floating container by the specified amount.\n */\nvoid container_floating_translate(struct sway_container *con,\n\t\tdouble x_amount, double y_amount);\n\n/**\n * Choose an output for the floating container's new position.\n */\nstruct sway_output *container_floating_find_output(struct sway_container *con);\n\n/**\n * Move a floating container to a new layout-local position.\n */\nvoid container_floating_move_to(struct sway_container *con,\n\t\tdouble lx, double ly);\n\n/**\n * Move a floating container to the center of the workspace.\n */\nvoid container_floating_move_to_center(struct sway_container *con);\n\nbool container_has_urgent_child(struct sway_container *container);\n\n/**\n * If the container is involved in a drag or resize operation via a mouse, this\n * ends the operation.\n */\nvoid container_end_mouse_operation(struct sway_container *container);\n\nvoid container_set_fullscreen(struct sway_container *con,\n\t\tenum sway_fullscreen_mode mode);\n\n/**\n * Convenience function.\n */\nvoid container_fullscreen_disable(struct sway_container *con);\n\n/**\n * Walk up the container tree branch starting at the given container, and return\n * its earliest ancestor.\n */\nstruct sway_container *container_toplevel_ancestor(\n\t\tstruct sway_container *container);\n\n/**\n * Return true if the container is floating, or a child of a floating split\n * container.\n */\nbool container_is_floating_or_child(struct sway_container *container);\n\n/**\n * Return true if the container is fullscreen, or a child of a fullscreen split\n * container.\n */\nbool container_is_fullscreen_or_child(struct sway_container *container);\n\nenum sway_container_layout container_parent_layout(struct sway_container *con);\n\nlist_t *container_get_siblings(struct sway_container *container);\n\nint container_sibling_index(struct sway_container *child);\n\nvoid container_handle_fullscreen_reparent(struct sway_container *con);\n\nvoid container_add_child(struct sway_container *parent,\n\t\tstruct sway_container *child);\n\nvoid container_insert_child(struct sway_container *parent,\n\t\tstruct sway_container *child, int i);\n\n/**\n * Side should be 0 to add before, or 1 to add after.\n */\nvoid container_add_sibling(struct sway_container *parent,\n\t\tstruct sway_container *child, bool after);\n\nvoid container_detach(struct sway_container *child);\n\nvoid container_replace(struct sway_container *container,\n\t\tstruct sway_container *replacement);\n\nvoid container_swap(struct sway_container *con1, struct sway_container *con2);\n\nstruct sway_container *container_split(struct sway_container *child,\n\t\tenum sway_container_layout layout);\n\nbool container_is_transient_for(struct sway_container *child,\n\t\tstruct sway_container *ancestor);\n\n/**\n * Find any container that has the given mark and return it.\n */\nstruct sway_container *container_find_mark(char *mark);\n\n/**\n * Find any container that has the given mark and remove the mark from the\n * container. Returns true if it matched a container.\n */\nbool container_find_and_unmark(char *mark);\n\n/**\n * Remove all marks from the container.\n */\nvoid container_clear_marks(struct sway_container *container);\n\nbool container_has_mark(struct sway_container *container, char *mark);\n\nvoid container_add_mark(struct sway_container *container, char *mark);\n\nvoid container_raise_floating(struct sway_container *con);\n\nbool container_is_scratchpad_hidden(struct sway_container *con);\n\nbool container_is_scratchpad_hidden_or_child(struct sway_container *con);\n\nbool container_is_sticky(struct sway_container *con);\n\nbool container_is_sticky_or_child(struct sway_container *con);\n\n/**\n * This will destroy pairs of redundant H/V splits\n * e.g. H[V[H[app app]] app] -> H[app app app]\n * The middle \"V[H[\" are eliminated by a call to container_squash\n * on the V[ con. It's grandchildren are added to its parent.\n *\n * This function is roughly equivalent to i3's tree_flatten here:\n * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651\n *\n * Returns the number of new containers added to the parent\n */\nint container_squash(struct sway_container *con);\n\nvoid container_arrange_title_bar(struct sway_container *con);\n\nvoid container_update(struct sway_container *con);\n\nvoid container_update_itself_and_parents(struct sway_container *con);\n\n#endif\n"
  },
  {
    "path": "include/sway/tree/node.h",
    "content": "#ifndef _SWAY_NODE_H\n#define _SWAY_NODE_H\n#include <wayland-server-core.h>\n#include <stdbool.h>\n#include <wlr/types/wlr_scene.h>\n#include \"list.h\"\n\n#define MIN_SANE_W 100\n#define MIN_SANE_H 60\n\nstruct sway_root;\nstruct sway_output;\nstruct sway_workspace;\nstruct sway_container;\nstruct sway_transaction_instruction;\nstruct wlr_box;\n\nenum sway_node_type {\n\tN_ROOT,\n\tN_OUTPUT,\n\tN_WORKSPACE,\n\tN_CONTAINER,\n};\n\nstruct sway_node {\n\tenum sway_node_type type;\n\tunion {\n\t\tstruct sway_root *sway_root;\n\t\tstruct sway_output *sway_output;\n\t\tstruct sway_workspace *sway_workspace;\n\t\tstruct sway_container *sway_container;\n\t};\n\n\t/**\n\t * A unique ID to identify this node.\n\t * Primarily used in the get_tree JSON output.\n\t */\n\tsize_t id;\n\n\tstruct sway_transaction_instruction *instruction;\n\tsize_t ntxnrefs;\n\tbool destroying;\n\n\t// If true, indicates that the container has pending state that differs from\n\t// the current.\n\tbool dirty;\n\n\tstruct {\n\t\tstruct wl_signal destroy;\n\t} events;\n};\n\nvoid node_init(struct sway_node *node, enum sway_node_type type, void *thing);\n\nconst char *node_type_to_str(enum sway_node_type type);\n\n/**\n * Mark a node as dirty if it isn't already. Dirty nodes will be included in the\n * next transaction then unmarked as dirty.\n */\nvoid node_set_dirty(struct sway_node *node);\n\nbool node_is_view(struct sway_node *node);\n\nchar *node_get_name(struct sway_node *node);\n\nvoid node_get_box(struct sway_node *node, struct wlr_box *box);\n\nstruct sway_output *node_get_output(struct sway_node *node);\n\nenum sway_container_layout node_get_layout(struct sway_node *node);\n\nstruct sway_node *node_get_parent(struct sway_node *node);\n\nlist_t *node_get_children(struct sway_node *node);\n\nbool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor);\n\n// when destroying a sway tree, it's not known which order the tree will be\n// destroyed. To prevent freeing of scene_nodes recursing up the tree,\n// let's use this helper function to disown them to the staging node.\nvoid scene_node_disown_children(struct wlr_scene_tree *tree);\n\n// a helper function used to allocate tree nodes. If an allocation failure\n// occurs a flag is flipped that can be checked later to destroy a parent\n// of this scene node preventing memory leaks.\nstruct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent,\n\t\tbool *failed);\n\n#endif\n"
  },
  {
    "path": "include/sway/tree/root.h",
    "content": "#ifndef _SWAY_ROOT_H\n#define _SWAY_ROOT_H\n#include <wayland-server-core.h>\n#include <wayland-util.h>\n#include <wlr/config.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_scene.h>\n#include <wlr/render/wlr_texture.h>\n#include \"sway/tree/container.h\"\n#include \"sway/tree/node.h\"\n#include \"list.h\"\n\nextern struct sway_root *root;\n\nstruct sway_root {\n\tstruct sway_node node;\n\tstruct wlr_output_layout *output_layout;\n\n\t// scene node layout:\n\t// - root\n\t// \t- staging\n\t// \t- layer shell stuff\n\t// \t- tiling\n\t// \t- floating\n\t// \t- fullscreen stuff\n\t// \t- seat stuff\n\t// \t- ext_session_lock\n\tstruct wlr_scene *root_scene;\n\n\t// since wlr_scene nodes can't be orphaned and must always\n\t// have a parent, use this staging scene_tree so that a\n\t// node always have a valid parent. Nothing in this\n\t// staging node will be visible.\n\tstruct wlr_scene_tree *staging;\n\n\t// tree containing all layers the compositor will render. Cursor handling\n\t// will end up iterating this tree.\n\tstruct wlr_scene_tree *layer_tree;\n\n\tstruct {\n\t\tstruct wlr_scene_tree *shell_background;\n\t\tstruct wlr_scene_tree *shell_bottom;\n\t\tstruct wlr_scene_tree *tiling;\n\t\tstruct wlr_scene_tree *floating;\n\t\tstruct wlr_scene_tree *shell_top;\n\t\tstruct wlr_scene_tree *fullscreen;\n\t\tstruct wlr_scene_tree *fullscreen_global;\n#if WLR_HAS_XWAYLAND\n\t\tstruct wlr_scene_tree *unmanaged;\n#endif\n\t\tstruct wlr_scene_tree *shell_overlay;\n\t\tstruct wlr_scene_tree *popup;\n\t\tstruct wlr_scene_tree *seat;\n\t\tstruct wlr_scene_tree *session_lock;\n\t} layers;\n\n\t// Includes disabled outputs\n\tstruct wl_list all_outputs; // sway_output::link\n\n\tdouble x, y;\n\tdouble width, height;\n\n\tlist_t *outputs; // struct sway_output\n\tlist_t *non_desktop_outputs; // struct sway_output_non_desktop\n\tlist_t *scratchpad; // struct sway_container\n\n\t// For when there's no connected outputs\n\tstruct sway_output *fallback_output;\n\n\tstruct sway_container *fullscreen_global;\n\n\tstruct {\n\t\tstruct wl_signal new_node;\n\t} events;\n};\n\nstruct sway_root *root_create(struct wl_display *display);\n\nvoid root_destroy(struct sway_root *root);\n\n/**\n * Move a container to the scratchpad.\n * If a workspace is passed, the container is assumed to have been in\n * the scratchpad before and is shown on the workspace.\n * The ws parameter can safely be NULL.\n */\nvoid root_scratchpad_add_container(struct sway_container *con,\n   struct sway_workspace *ws);\n\n/**\n * Remove a container from the scratchpad.\n */\nvoid root_scratchpad_remove_container(struct sway_container *con);\n\n/**\n * Show a single scratchpad container.\n */\nvoid root_scratchpad_show(struct sway_container *con);\n\n/**\n * Hide a single scratchpad container.\n */\nvoid root_scratchpad_hide(struct sway_container *con);\n\nvoid root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),\n\t\tvoid *data);\n\nvoid root_for_each_container(void (*f)(struct sway_container *con, void *data),\n\t\tvoid *data);\n\nstruct sway_output *root_find_output(\n\t\tbool (*test)(struct sway_output *output, void *data), void *data);\n\nstruct sway_workspace *root_find_workspace(\n\t\tbool (*test)(struct sway_workspace *ws, void *data), void *data);\n\nstruct sway_container *root_find_container(\n\t\tbool (*test)(struct sway_container *con, void *data), void *data);\n\nvoid root_get_box(struct sway_root *root, struct wlr_box *box);\n\n#endif\n"
  },
  {
    "path": "include/sway/tree/view.h",
    "content": "#ifndef _SWAY_VIEW_H\n#define _SWAY_VIEW_H\n#include <wayland-server-core.h>\n#include <wlr/config.h>\n#include <wlr/types/wlr_compositor.h>\n#include <wlr/types/wlr_scene.h>\n#include <wlr/types/wlr_tearing_control_v1.h>\n#include \"sway/config.h\"\n#if WLR_HAS_XWAYLAND\n#include <wlr/xwayland.h>\n#endif\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n\nstruct sway_container;\nstruct sway_xdg_decoration;\n\nenum sway_view_type {\n\tSWAY_VIEW_XDG_SHELL,\n#if WLR_HAS_XWAYLAND\n\tSWAY_VIEW_XWAYLAND,\n#endif\n};\n\nenum sway_view_prop {\n\tVIEW_PROP_TITLE,\n\tVIEW_PROP_APP_ID,\n\tVIEW_PROP_TAG,\n\tVIEW_PROP_CLASS,\n\tVIEW_PROP_INSTANCE,\n\tVIEW_PROP_WINDOW_TYPE,\n\tVIEW_PROP_WINDOW_ROLE,\n#if WLR_HAS_XWAYLAND\n\tVIEW_PROP_X11_WINDOW_ID,\n\tVIEW_PROP_X11_PARENT_ID,\n#endif\n};\n\nenum sway_view_tearing_mode {\n\tTEARING_OVERRIDE_FALSE,\n\tTEARING_OVERRIDE_TRUE,\n\tTEARING_WINDOW_HINT,\n};\n\nstruct sway_view_impl {\n\tvoid (*get_constraints)(struct sway_view *view, double *min_width,\n\t\t\tdouble *max_width, double *min_height, double *max_height);\n\tconst char *(*get_string_prop)(struct sway_view *view,\n\t\t\tenum sway_view_prop prop);\n\tuint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop);\n\tuint32_t (*configure)(struct sway_view *view, double lx, double ly,\n\t\t\tint width, int height);\n\tvoid (*set_activated)(struct sway_view *view, bool activated);\n\tvoid (*set_tiled)(struct sway_view *view, bool tiled);\n\tvoid (*set_fullscreen)(struct sway_view *view, bool fullscreen);\n\tvoid (*set_resizing)(struct sway_view *view, bool resizing);\n\tbool (*wants_floating)(struct sway_view *view);\n\tbool (*is_transient_for)(struct sway_view *child,\n\t\t\tstruct sway_view *ancestor);\n\tvoid (*close)(struct sway_view *view);\n\tvoid (*close_popups)(struct sway_view *view);\n\tvoid (*destroy)(struct sway_view *view);\n};\n\nstruct sway_view {\n\tenum sway_view_type type;\n\tconst struct sway_view_impl *impl;\n\n\tstruct wlr_scene_tree *scene_tree;\n\tstruct wlr_scene_tree *content_tree;\n\tstruct wlr_scene_tree *saved_surface_tree;\n\n\tstruct wlr_scene *image_capture_scene;\n\tstruct wlr_ext_image_capture_source_v1 *image_capture_source;\n\n\tstruct sway_container *container; // NULL if unmapped and transactions finished\n\tstruct wlr_surface *surface; // NULL for unmapped views\n\tstruct sway_xdg_decoration *xdg_decoration;\n\n\tpid_t pid;\n\tstruct launcher_ctx *ctx;\n\n\t// The size the view would want to be if it weren't tiled.\n\t// Used when changing a view from tiled to floating.\n\tint natural_width, natural_height;\n\n\tbool using_csd;\n\n\tstruct timespec urgent;\n\tbool allow_request_urgent;\n\tstruct wl_event_source *urgent_timer;\n\n\t// The geometry for whatever the client is committing, regardless of\n\t// transaction state. Updated on every commit.\n\tstruct wlr_box geometry;\n\n\tstruct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel;\n\n\tstruct wlr_foreign_toplevel_handle_v1 *foreign_toplevel;\n\tstruct wl_listener foreign_activate_request;\n\tstruct wl_listener foreign_fullscreen_request;\n\tstruct wl_listener foreign_close_request;\n\tstruct wl_listener foreign_destroy;\n\n\tbool destroying;\n\n\tlist_t *executed_criteria; // struct criteria *\n\n\tunion {\n\t\tstruct wlr_xdg_toplevel *wlr_xdg_toplevel;\n#if WLR_HAS_XWAYLAND\n\t\tstruct wlr_xwayland_surface *wlr_xwayland_surface;\n#endif\n\t};\n\n\tstruct {\n\t\tstruct wl_signal unmap;\n\t} events;\n\n\tint max_render_time; // In milliseconds\n\n\tenum seat_config_shortcuts_inhibit shortcuts_inhibit;\n\n\tenum sway_view_tearing_mode tearing_mode;\n\tenum wp_tearing_control_v1_presentation_hint tearing_hint;\n};\n\nstruct sway_xdg_shell_view {\n\tstruct sway_view view;\n\n\tstruct wlr_scene_tree *image_capture_tree;\n\tchar *tag;\n\n\tstruct wl_listener commit;\n\tstruct wl_listener request_move;\n\tstruct wl_listener request_resize;\n\tstruct wl_listener request_maximize;\n\tstruct wl_listener request_fullscreen;\n\tstruct wl_listener set_title;\n\tstruct wl_listener set_app_id;\n\tstruct wl_listener new_popup;\n\tstruct wl_listener map;\n\tstruct wl_listener unmap;\n\tstruct wl_listener destroy;\n};\n#if WLR_HAS_XWAYLAND\nstruct sway_xwayland_view {\n\tstruct sway_view view;\n\n\tstruct wlr_scene_tree *surface_tree;\n\n\tstruct wlr_scene_surface *image_capture_scene_surface;\n\n\tstruct wl_listener commit;\n\tstruct wl_listener request_move;\n\tstruct wl_listener request_resize;\n\tstruct wl_listener request_maximize;\n\tstruct wl_listener request_minimize;\n\tstruct wl_listener request_configure;\n\tstruct wl_listener request_fullscreen;\n\tstruct wl_listener request_activate;\n\tstruct wl_listener set_title;\n\tstruct wl_listener set_class;\n\tstruct wl_listener set_role;\n\tstruct wl_listener set_startup_id;\n\tstruct wl_listener set_window_type;\n\tstruct wl_listener set_hints;\n\tstruct wl_listener set_decorations;\n\tstruct wl_listener associate;\n\tstruct wl_listener dissociate;\n\tstruct wl_listener map;\n\tstruct wl_listener unmap;\n\tstruct wl_listener destroy;\n\tstruct wl_listener override_redirect;\n\n\tstruct wl_listener surface_tree_destroy;\n};\n\nstruct sway_xwayland_unmanaged {\n\tstruct wlr_xwayland_surface *wlr_xwayland_surface;\n\n\tstruct wlr_scene_surface *surface_scene;\n\n\tstruct wl_listener request_activate;\n\tstruct wl_listener request_configure;\n\tstruct wl_listener request_fullscreen;\n\tstruct wl_listener set_geometry;\n\tstruct wl_listener associate;\n\tstruct wl_listener dissociate;\n\tstruct wl_listener map;\n\tstruct wl_listener unmap;\n\tstruct wl_listener destroy;\n\tstruct wl_listener override_redirect;\n};\n#endif\n\nstruct sway_popup_desc {\n\tstruct wlr_scene_node *relative;\n\tstruct sway_view *view;\n};\n\nstruct sway_xdg_popup {\n\tstruct sway_view *view;\n\tstruct wlr_xdg_popup *wlr_xdg_popup;\n\n\tstruct wlr_scene_tree *scene_tree;\n\tstruct wlr_scene_tree *xdg_surface_tree;\n\n\tstruct wlr_scene_tree *image_capture_tree;\n\n\tstruct sway_popup_desc desc;\n\n\tstruct wl_listener surface_commit;\n\tstruct wl_listener new_popup;\n\tstruct wl_listener reposition;\n\tstruct wl_listener destroy;\n};\n\nconst char *view_get_title(struct sway_view *view);\n\nconst char *view_get_app_id(struct sway_view *view);\n\nconst char *view_get_class(struct sway_view *view);\n\nconst char *view_get_instance(struct sway_view *view);\n\nuint32_t view_get_x11_window_id(struct sway_view *view);\n\nuint32_t view_get_x11_parent_id(struct sway_view *view);\n\nconst char *view_get_window_role(struct sway_view *view);\n\nuint32_t view_get_window_type(struct sway_view *view);\n\nconst char *view_get_sandbox_engine(struct sway_view *view);\n\nconst char *view_get_sandbox_app_id(struct sway_view *view);\n\nconst char *view_get_sandbox_instance_id(struct sway_view *view);\n\nconst char *view_get_tag(struct sway_view *view);\n\nconst char *view_get_shell(struct sway_view *view);\n\nvoid view_get_constraints(struct sway_view *view, double *min_width,\n\t\tdouble *max_width, double *min_height, double *max_height);\n\nuint32_t view_configure(struct sway_view *view, double lx, double ly, int width,\n\tint height);\n\nbool view_inhibit_idle(struct sway_view *view);\n\n/**\n * Whether or not this view's most distant ancestor (possibly itself) is the\n * only visible node in its tree. If the view is tiling, there may be floating\n * views. If the view is floating, there may be tiling views or views in a\n * different floating container.\n */\nbool view_ancestor_is_only_visible(struct sway_view *view);\n\n/**\n * Configure the view's position and size based on the container's position and\n * size, taking borders into consideration.\n */\nvoid view_autoconfigure(struct sway_view *view);\n\nvoid view_set_activated(struct sway_view *view, bool activated);\n\n/**\n * Called when the view requests to be focused.\n */\nvoid view_request_activate(struct sway_view *view, struct sway_seat *seat);\n\n/*\n * Called when the view requests urgent state\n */\nvoid view_request_urgent(struct sway_view *view);\n\n/**\n * If possible, instructs the client to change their decoration mode.\n */\nvoid view_set_csd_from_server(struct sway_view *view, bool enabled);\n\n/**\n * Updates the view's border setting when the client unexpectedly changes their\n * decoration mode.\n */\nvoid view_update_csd_from_client(struct sway_view *view, bool enabled);\n\nvoid view_set_tiled(struct sway_view *view, bool tiled);\n\nvoid view_close(struct sway_view *view);\n\nvoid view_close_popups(struct sway_view *view);\n\n// view implementation\n\nbool view_init(struct sway_view *view, enum sway_view_type type,\n\tconst struct sway_view_impl *impl);\n\nvoid view_destroy(struct sway_view *view);\n\nvoid view_begin_destroy(struct sway_view *view);\n\n/**\n * Map a view, ie. make it visible in the tree.\n *\n * `fullscreen` should be set to true (and optionally `fullscreen_output`\n * should be populated) if the view should be made fullscreen immediately.\n *\n * `decoration` should be set to true if the client prefers CSD. The client's\n * preference may be ignored.\n */\nvoid view_map(struct sway_view *view, struct wlr_surface *wlr_surface,\n\tbool fullscreen, struct wlr_output *fullscreen_output, bool decoration);\n\nvoid view_unmap(struct sway_view *view);\n\nvoid view_update_size(struct sway_view *view);\nvoid view_center_and_clip_surface(struct sway_view *view);\n\nstruct sway_view *view_from_wlr_xdg_surface(\n\tstruct wlr_xdg_surface *xdg_surface);\n#if WLR_HAS_XWAYLAND\nstruct sway_view *view_from_wlr_xwayland_surface(\n\tstruct wlr_xwayland_surface *xsurface);\n#endif\nstruct sway_view *view_from_wlr_surface(struct wlr_surface *surface);\n\nvoid view_update_app_id(struct sway_view *view);\n\n/**\n * Re-read the view's title property and update any relevant title bars.\n * The force argument makes it recreate the title bars even if the title hasn't\n * changed.\n */\nvoid view_update_title(struct sway_view *view, bool force);\n\n/**\n * Run any criteria that match the view and haven't been run on this view\n * before.\n */\nvoid view_execute_criteria(struct sway_view *view);\n\n/**\n * Returns true if there's a possibility the view may be rendered on screen.\n * Intended for damage tracking.\n */\nbool view_is_visible(struct sway_view *view);\n\nvoid view_set_urgent(struct sway_view *view, bool enable);\n\nbool view_is_urgent(struct sway_view *view);\n\nvoid view_remove_saved_buffer(struct sway_view *view);\n\nvoid view_save_buffer(struct sway_view *view);\n\nbool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);\n\nvoid view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);\n\nvoid view_send_frame_done(struct sway_view *view);\n\nbool view_can_tear(struct sway_view *view);\n\nvoid xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data);\n\n#endif\n"
  },
  {
    "path": "include/sway/tree/workspace.h",
    "content": "#ifndef _SWAY_WORKSPACE_H\n#define _SWAY_WORKSPACE_H\n\n#include <stdbool.h>\n#include <wlr/types/wlr_scene.h>\n#include \"sway/config.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/node.h\"\n\nstruct sway_view;\n\nstruct sway_workspace_state {\n\tstruct sway_container *fullscreen;\n\tdouble x, y;\n\tint width, height;\n\tenum sway_container_layout layout;\n\tstruct sway_output *output;\n\tlist_t *floating;\n\tlist_t *tiling;\n\n\tstruct sway_container *focused_inactive_child;\n\tbool focused;\n};\n\nstruct sway_workspace {\n\tstruct sway_node node;\n\n\tstruct {\n\t\tstruct wlr_scene_tree *tiling;\n\t\tstruct wlr_scene_tree *fullscreen;\n\t} layers;\n\n\tstruct sway_container *fullscreen;\n\n\tchar *name;\n\tchar *representation;\n\n\tdouble x, y;\n\tint width, height;\n\tenum sway_container_layout layout;\n\tenum sway_container_layout prev_split_layout;\n\n\tstruct side_gaps current_gaps;\n\tint gaps_inner;\n\tstruct side_gaps gaps_outer;\n\n\tstruct sway_output *output; // NULL if no outputs are connected\n\tlist_t *floating;           // struct sway_container\n\tlist_t *tiling;             // struct sway_container\n\tlist_t *output_priority;\n\tbool urgent;\n\n\tstruct sway_workspace_state current;\n};\n\nstruct workspace_config *workspace_find_config(const char *ws_name);\n\nstruct sway_output *workspace_get_initial_output(const char *name);\n\nstruct sway_workspace *workspace_create(struct sway_output *output,\n\t\tconst char *name);\n\nvoid workspace_destroy(struct sway_workspace *workspace);\n\nvoid workspace_begin_destroy(struct sway_workspace *workspace);\n\nvoid workspace_consider_destroy(struct sway_workspace *ws);\n\nchar *workspace_next_name(const char *output_name);\n\nstruct sway_workspace *workspace_auto_back_and_forth(\n\t\tstruct sway_workspace *workspace);\n\nbool workspace_switch(struct sway_workspace *workspace);\n\nstruct sway_workspace *workspace_by_number(const char* name);\n\nstruct sway_workspace *workspace_by_name(const char*);\n\nstruct sway_workspace *workspace_output_next(struct sway_workspace *current);\n\nstruct sway_workspace *workspace_next(struct sway_workspace *current);\n\nstruct sway_workspace *workspace_output_prev(struct sway_workspace *current);\n\nstruct sway_workspace *workspace_prev(struct sway_workspace *current);\n\nbool workspace_is_visible(struct sway_workspace *ws);\n\nbool workspace_is_empty(struct sway_workspace *ws);\n\nvoid workspace_output_raise_priority(struct sway_workspace *workspace,\n\t\tstruct sway_output *old_output, struct sway_output *new_output);\n\nvoid workspace_output_add_priority(struct sway_workspace *workspace,\n\t\tstruct sway_output *output);\n\nstruct sway_output *workspace_output_get_highest_available(\n\t\tstruct sway_workspace *ws);\n\nvoid workspace_detect_urgent(struct sway_workspace *workspace);\n\nvoid workspace_for_each_container(struct sway_workspace *ws,\n\t\tvoid (*f)(struct sway_container *con, void *data), void *data);\n\nstruct sway_container *workspace_find_container(struct sway_workspace *ws,\n\t\tbool (*test)(struct sway_container *con, void *data), void *data);\n\n/**\n * Wrap the workspace's tiling children in a new container.\n * The new container will be the only direct tiling child of the workspace.\n * The new container is returned.\n */\nstruct sway_container *workspace_wrap_children(struct sway_workspace *ws);\n\nvoid workspace_unwrap_children(struct sway_workspace *ws,\n\t\tstruct sway_container *wrap);\n\nvoid workspace_detach(struct sway_workspace *workspace);\n\nstruct sway_container *workspace_add_tiling(struct sway_workspace *workspace,\n\t\tstruct sway_container *con);\n\nvoid workspace_add_floating(struct sway_workspace *workspace,\n\t\tstruct sway_container *con);\n\n/**\n * Adds a tiling container to the workspace without considering\n * the workspace_layout, so the con will not be split.\n */\nvoid workspace_insert_tiling_direct(struct sway_workspace *workspace,\n\t\tstruct sway_container *con, int index);\n\nstruct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,\n\t\tstruct sway_container *con, int index);\n\nvoid workspace_remove_gaps(struct sway_workspace *ws);\n\nvoid workspace_add_gaps(struct sway_workspace *ws);\n\nstruct sway_container *workspace_split(struct sway_workspace *workspace,\n\t\tenum sway_container_layout layout);\n\nvoid workspace_update_representation(struct sway_workspace *ws);\n\nvoid workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box);\n\nsize_t workspace_num_tiling_views(struct sway_workspace *ws);\n\nsize_t workspace_num_sticky_containers(struct sway_workspace *ws);\n\n/**\n * workspace_squash is container_flatten in the reverse\n * direction. Instead of eliminating redundant splits that are\n * parents of the target container, it eliminates pairs of\n * redundant H/V splits that are children of the workspace.\n */\nvoid workspace_squash(struct sway_workspace *workspace);\n\n#endif\n"
  },
  {
    "path": "include/sway/xdg_decoration.h",
    "content": "#ifndef _SWAY_XDG_DECORATION_H\n#define _SWAY_XDG_DECORATION_H\n\n#include <wlr/types/wlr_xdg_decoration_v1.h>\n\nstruct sway_xdg_decoration {\n\tstruct wlr_xdg_toplevel_decoration_v1 *wlr_xdg_decoration;\n\tstruct wl_list link;\n\n\tstruct sway_view *view;\n\n\tstruct wl_listener destroy;\n\tstruct wl_listener request_mode;\n};\n\nstruct sway_xdg_decoration *xdg_decoration_from_surface(\n\tstruct wlr_surface *surface);\n\nvoid set_xdg_decoration_mode(struct sway_xdg_decoration *deco);\n\n#endif\n"
  },
  {
    "path": "include/sway/xwayland.h",
    "content": "#ifndef SWAY_XWAYLAND_H\n#define SWAY_XWAYLAND_H\n\n#include <wlr/xwayland.h>\n#include <xcb/xproto.h>\n\nenum atom_name {\n\tNET_WM_WINDOW_TYPE_NORMAL,\n\tNET_WM_WINDOW_TYPE_DIALOG,\n\tNET_WM_WINDOW_TYPE_UTILITY,\n\tNET_WM_WINDOW_TYPE_TOOLBAR,\n\tNET_WM_WINDOW_TYPE_SPLASH,\n\tNET_WM_WINDOW_TYPE_MENU,\n\tNET_WM_WINDOW_TYPE_DROPDOWN_MENU,\n\tNET_WM_WINDOW_TYPE_POPUP_MENU,\n\tNET_WM_WINDOW_TYPE_TOOLTIP,\n\tNET_WM_WINDOW_TYPE_NOTIFICATION,\n\tNET_WM_STATE_MODAL,\n\tATOM_LAST,\n};\n\nstruct sway_xwayland {\n\tstruct wlr_xwayland *wlr_xwayland;\n\tstruct wlr_xcursor_manager *xcursor_manager;\n\n\txcb_atom_t atoms[ATOM_LAST];\n};\n\nvoid handle_xwayland_ready(struct wl_listener *listener, void *data);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/bar.h",
    "content": "#ifndef _SWAYBAR_BAR_H\n#define _SWAYBAR_BAR_H\n#include <wayland-client.h>\n#include \"config.h\"\n#include \"input.h\"\n#include \"pool-buffer.h\"\n#include \"cursor-shape-v1-client-protocol.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n#include \"xdg-output-unstable-v1-client-protocol.h\"\n\nstruct swaybar_config;\nstruct swaybar_output;\n#if HAVE_TRAY\nstruct swaybar_tray;\n#endif\nstruct swaybar_workspace;\nstruct loop;\n\nstruct swaybar {\n\tchar *id;\n\tchar *mode;\n\tbool mode_pango_markup;\n\n\t// only relevant when bar is in \"hide\" mode\n\tbool visible_by_modifier;\n\tbool visible_by_urgency;\n\tbool visible_by_mode;\n\tbool visible;\n\n\tstruct wl_display *display;\n\tstruct wl_compositor *compositor;\n\tstruct zwlr_layer_shell_v1 *layer_shell;\n\tstruct zxdg_output_manager_v1 *xdg_output_manager;\n\tstruct wp_cursor_shape_manager_v1 *cursor_shape_manager;\n\tstruct wl_shm *shm;\n\n\tstruct swaybar_config *config;\n\tstruct status_line *status;\n\n\tstruct loop *eventloop;\n\n\tint ipc_event_socketfd;\n\tint ipc_socketfd;\n\n\tstruct wl_list outputs; // swaybar_output::link\n\tstruct wl_list unused_outputs; // swaybar_output::link\n\tstruct wl_list seats; // swaybar_seat::link\n\n#if HAVE_TRAY\n\tstruct swaybar_tray *tray;\n#endif\n\n\tbool running;\n};\n\nstruct swaybar_output {\n\tstruct wl_list link; // swaybar::outputs\n\tstruct swaybar *bar;\n\tstruct wl_output *output;\n\tstruct zxdg_output_v1 *xdg_output;\n\tstruct wl_surface *surface;\n\tstruct zwlr_layer_surface_v1 *layer_surface;\n\tuint32_t wl_name;\n\n\tstruct wl_list workspaces; // swaybar_workspace::link\n\tstruct wl_list hotspots; // swaybar_hotspot::link\n\n\tchar *name;\n\tchar *identifier;\n\tbool focused;\n\n\tuint32_t width, height;\n\tint32_t scale;\n\tenum wl_output_subpixel subpixel;\n\tstruct pool_buffer buffers[2];\n\tstruct pool_buffer *current_buffer;\n\tbool dirty;\n\tbool frame_scheduled;\n\n\tuint32_t output_height, output_width, output_x, output_y;\n};\n\nstruct swaybar_workspace {\n\tstruct wl_list link; // swaybar_output::workspaces\n\tint num;\n\tchar *name;\n\tchar *label;\n\tbool focused;\n\tbool visible;\n\tbool urgent;\n};\n\nbool bar_setup(struct swaybar *bar, const char *socket_path);\nvoid bar_run(struct swaybar *bar);\nvoid bar_teardown(struct swaybar *bar);\n\nvoid set_bar_dirty(struct swaybar *bar);\n\n/*\n * Determines whether the bar should be visible and changes it to be so.\n * If the current visibility of the bar is the different to what it should be,\n * then it adds or destroys the layer surface as required,\n * as well as sending the cont or stop signal to the status command.\n * If the current visibility of the bar is already what it should be,\n * then this function is a no-op, unless moving_layer is true, which occurs\n * when the bar changes from \"hide\" to \"dock\" mode or vice versa, and the bar\n * needs to be destroyed and re-added in order to change its layer.\n *\n * Returns true if the bar is now visible, otherwise false.\n */\nbool determine_bar_visibility(struct swaybar *bar, bool moving_layer);\nvoid free_workspaces(struct wl_list *list);\n\nvoid status_in(int fd, short mask, void *data);\n\nvoid destroy_layer_surface(struct swaybar_output *output);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/config.h",
    "content": "#ifndef _SWAYBAR_CONFIG_H\n#define _SWAYBAR_CONFIG_H\n#include <stdbool.h>\n#include <stdint.h>\n#include <wayland-client.h>\n#include \"../include/config.h\"\n#include \"list.h\"\n#include \"util.h\"\n#include <pango/pangocairo.h>\n\nstruct box_colors {\n\tuint32_t border;\n\tuint32_t background;\n\tuint32_t text;\n};\n\nstruct box_size {\n\tuint32_t width;\n\tuint32_t height;\n};\n\nstruct config_output {\n\tstruct wl_list link; // swaybar_config::outputs\n\tchar *name;\n};\n\nstruct swaybar_binding {\n\tuint32_t button;\n\tchar *command;\n\tbool release;\n};\n\nstruct swaybar_config {\n\tchar *status_command;\n\tbool pango_markup;\n\tuint32_t position; // zwlr_layer_surface_v1_anchor\n\tPangoFontDescription *font_description;\n\tchar *sep_symbol;\n\tchar *mode;\n\tchar *hidden_state;\n\tchar *modifier;\n\tbool strip_workspace_numbers;\n\tbool strip_workspace_name;\n\tbool binding_mode_indicator;\n\tbool wrap_scroll;\n\tbool workspace_buttons;\n\tuint32_t workspace_min_width;\n\tlist_t *bindings;\n\tstruct wl_list outputs; // config_output::link\n\tint height;\n\tint status_padding;\n\tint status_edge_padding;\n\tstruct {\n\t\tint top;\n\t\tint right;\n\t\tint bottom;\n\t\tint left;\n\t} gaps;\n\n\tstruct {\n\t\tuint32_t background;\n\t\tuint32_t statusline;\n\t\tuint32_t separator;\n\n\t\tuint32_t focused_background;\n\t\tuint32_t focused_statusline;\n\t\tuint32_t focused_separator;\n\n\t\tstruct box_colors focused_workspace;\n\t\tstruct box_colors active_workspace;\n\t\tstruct box_colors inactive_workspace;\n\t\tstruct box_colors urgent_workspace;\n\t\tstruct box_colors binding_mode;\n\t} colors;\n\n#if HAVE_TRAY\n\tchar *icon_theme;\n\tstruct wl_list tray_bindings; // struct tray_binding::link\n\tbool tray_hidden;\n\tlist_t *tray_outputs; // char *\n\tint tray_padding;\n#endif\n};\n\n#if HAVE_TRAY\nstruct tray_binding {\n\tuint32_t button;\n\tchar *command;\n\tstruct wl_list link; // struct tray_binding::link\n};\n\nvoid free_tray_binding(struct tray_binding *binding);\n#endif\n\nstruct swaybar_config *init_config(void);\nvoid free_config(struct swaybar_config *config);\nuint32_t parse_position(const char *position);\nvoid free_binding(struct swaybar_binding *binding);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/i3bar.h",
    "content": "#ifndef _SWAYBAR_I3BAR_H\n#define _SWAYBAR_I3BAR_H\n\n#include \"input.h\"\n#include \"status_line.h\"\n\nstruct i3bar_block {\n\tstruct wl_list link; // status_link::blocks\n\tint ref_count;\n\tchar *full_text, *short_text, *align, *min_width_str;\n\tbool urgent;\n\tuint32_t color;\n\tbool color_set;\n\tint min_width;\n\tchar *name, *instance;\n\tbool separator;\n\tint separator_block_width;\n\tbool markup;\n\t// Airblader features\n\tuint32_t background;\n\tuint32_t border;\n\tbool border_set;\n\tint border_top;\n\tint border_bottom;\n\tint border_left;\n\tint border_right;\n};\n\nvoid i3bar_block_unref(struct i3bar_block *block);\nbool i3bar_handle_readable(struct status_line *status);\nenum hotspot_event_handling i3bar_block_send_click(struct status_line *status,\n\t\tstruct i3bar_block *block, double x, double y, double rx, double ry,\n\t\tdouble w, double h, int scale, uint32_t button, bool released);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/image.h",
    "content": "#ifndef _SWAYBAR_IMAGE_H\n#define _SWAYBAR_IMAGE_H\n#include <cairo.h>\n\ncairo_surface_t *load_image(const char *path);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/input.h",
    "content": "#ifndef _SWAYBAR_INPUT_H\n#define _SWAYBAR_INPUT_H\n\n#include <wayland-client.h>\n#include <stdbool.h>\n#include \"list.h\"\n\n#define SWAY_SCROLL_UP KEY_MAX + 1\n#define SWAY_SCROLL_DOWN KEY_MAX + 2\n#define SWAY_SCROLL_LEFT KEY_MAX + 3\n#define SWAY_SCROLL_RIGHT KEY_MAX + 4\n\n#define SWAY_CONTINUOUS_SCROLL_TIMEOUT 1000\n#define SWAY_CONTINUOUS_SCROLL_THRESHOLD 10000\n\nstruct swaybar;\nstruct swaybar_output;\n\nstruct swaybar_pointer {\n\tstruct wl_pointer *pointer;\n\tstruct wl_cursor_theme *cursor_theme;\n\tstruct wl_cursor_image *cursor_image;\n\tstruct wl_surface *cursor_surface;\n\tstruct swaybar_output *current;\n\tdouble x, y;\n\tuint32_t serial;\n};\n\nstruct touch_slot {\n\tint32_t id;\n\tuint32_t time;\n\tstruct swaybar_output *output;\n\tdouble start_x, start_y;\n\tdouble x, y;\n};\n\nstruct swaybar_touch {\n\tstruct wl_touch *touch;\n\tstruct touch_slot slots[16];\n};\n\nenum hotspot_event_handling {\n\tHOTSPOT_IGNORE,\n\tHOTSPOT_PROCESS,\n};\n\nstruct swaybar_hotspot {\n\tstruct wl_list link; // swaybar_output::hotspots\n\tint x, y, width, height;\n\tenum hotspot_event_handling (*callback)(struct swaybar_output *output,\n\t\tstruct swaybar_hotspot *hotspot, double x, double y, uint32_t button,\n\t\tbool released, void *data);\n\tvoid (*destroy)(void *data);\n\tvoid *data;\n};\n\nstruct swaybar_scroll_axis {\n\twl_fixed_t value;\n\tuint32_t discrete_steps;\n\tuint32_t update_time;\n};\n\nstruct swaybar_seat {\n\tstruct swaybar *bar;\n\tuint32_t wl_name;\n\tstruct wl_seat *wl_seat;\n\tstruct swaybar_pointer pointer;\n\tstruct swaybar_touch touch;\n\tstruct wl_list link; // swaybar_seat:link\n\tstruct swaybar_scroll_axis axis[2];\n};\n\nextern const struct wl_seat_listener seat_listener;\n\nvoid update_cursor(struct swaybar_seat *seat);\n\nuint32_t event_to_x11_button(uint32_t event);\n\nvoid free_hotspots(struct wl_list *list);\n\nvoid swaybar_seat_free(struct swaybar_seat *seat);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/ipc.h",
    "content": "#ifndef _SWAYBAR_IPC_H\n#define _SWAYBAR_IPC_H\n#include <stdbool.h>\n#include \"swaybar/bar.h\"\n\nbool ipc_initialize(struct swaybar *bar);\nbool handle_ipc_readable(struct swaybar *bar);\nbool ipc_get_workspaces(struct swaybar *bar);\nvoid ipc_send_workspace_command(struct swaybar *bar, const char *ws);\nvoid ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/render.h",
    "content": "#ifndef _SWAYBAR_RENDER_H\n#define _SWAYBAR_RENDER_H\n\nstruct swaybar_output;\n\nvoid render_frame(struct swaybar_output *output);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/status_line.h",
    "content": "#ifndef _SWAYBAR_STATUS_LINE_H\n#define _SWAYBAR_STATUS_LINE_H\n#include <json.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include \"bar.h\"\n\nenum status_protocol {\n\tPROTOCOL_UNDEF,\n\tPROTOCOL_ERROR,\n\tPROTOCOL_TEXT,\n\tPROTOCOL_I3BAR,\n};\n\nstruct status_line {\n\tstruct swaybar *bar;\n\n\tpid_t pid;\n\tint read_fd, write_fd;\n\tFILE *read, *write;\n\n\tenum status_protocol protocol;\n\tconst char *text;\n\tstruct wl_list blocks; // i3bar_block::link\n\n\tint stop_signal;\n\tint cont_signal;\n\n\tbool click_events;\n\tbool float_event_coords;\n\tbool clicked;\n\tchar *buffer;\n\tsize_t buffer_size;\n\tsize_t buffer_index;\n\tbool started;\n\tbool expecting_comma;\n\tjson_tokener *tokener;\n};\n\nstruct status_line *status_line_init(char *cmd);\nvoid status_error(struct status_line *status, const char *text);\nbool status_handle_readable(struct status_line *status);\nvoid status_line_free(struct status_line *status);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/tray/host.h",
    "content": "#ifndef _SWAYBAR_TRAY_HOST_H\n#define _SWAYBAR_TRAY_HOST_H\n\n#include <stdbool.h>\n\nstruct swaybar_tray;\n\nstruct swaybar_host {\n\tstruct swaybar_tray *tray;\n\tchar *service;\n\tchar *watcher_interface;\n};\n\nbool init_host(struct swaybar_host *host, char *protocol, struct swaybar_tray *tray);\nvoid finish_host(struct swaybar_host *host);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/tray/icon.h",
    "content": "#ifndef _SWAYBAR_TRAY_ICON_H\n#define _SWAYBAR_TRAY_ICON_H\n\n#include \"list.h\"\n\nstruct icon_theme_subdir {\n\tchar *name;\n\tint size;\n\n\tenum {\n\t\tTHRESHOLD,\n\t\tSCALABLE,\n\t\tFIXED\n\t} type;\n\n\tint max_size;\n\tint min_size;\n\tint threshold;\n};\n\nstruct icon_theme {\n\tchar *name;\n\tchar *comment;\n\tlist_t *inherits; // char *\n\tlist_t *directories; // char *\n\n\tchar *dir;\n\tlist_t *subdirs; // struct icon_theme_subdir *\n};\n\nvoid init_themes(list_t **themes, list_t **basedirs);\nvoid finish_themes(list_t *themes, list_t *basedirs);\n\n/*\n * Finds an icon of a specified size given a list of themes and base directories.\n * If the icon is found, the pointers min_size & max_size are set to minimum &\n * maximum size that the icon can be scaled to, respectively.\n * Returns: path of icon (which should be freed), or NULL if the icon is not found.\n */\nchar *find_icon(list_t *themes, list_t *basedirs, char *name, int size,\n\t\tchar *theme, int *min_size, int *max_size);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/tray/item.h",
    "content": "#ifndef _SWAYBAR_TRAY_ITEM_H\n#define _SWAYBAR_TRAY_ITEM_H\n\n#include <cairo.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <wayland-util.h>\n#include \"swaybar/tray/tray.h\"\n#include \"list.h\"\n\nstruct swaybar_output;\n\nstruct swaybar_pixmap {\n\tint size;\n\tunsigned char pixels[];\n};\n\nstruct swaybar_sni_slot {\n\tstruct wl_list link; // swaybar_sni::slots\n\tstruct swaybar_sni *sni;\n\tconst char *prop;\n\tconst char *type;\n\tvoid *dest;\n\tsd_bus_slot *slot;\n};\n\nstruct swaybar_sni {\n\t// icon properties\n\tstruct swaybar_tray *tray;\n\tcairo_surface_t *icon;\n\tint min_size;\n\tint max_size;\n\tint target_size;\n\n\t// dbus properties\n\tchar *watcher_id;\n\tchar *service;\n\tchar *path;\n\tchar *interface;\n\n\tchar *status;\n\tchar *icon_name;\n\tlist_t *icon_pixmap; // struct swaybar_pixmap *\n\tchar *attention_icon_name;\n\tlist_t *attention_icon_pixmap; // struct swaybar_pixmap *\n\tbool item_is_menu;\n\tchar *menu;\n\tchar *icon_theme_path; // non-standard KDE property\n\n\tstruct wl_list slots; // swaybar_sni_slot::link\n};\n\nstruct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray);\nvoid destroy_sni(struct swaybar_sni *sni);\nuint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,\n\t\tstruct swaybar_sni *sni);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/tray/tray.h",
    "content": "#ifndef _SWAYBAR_TRAY_TRAY_H\n#define _SWAYBAR_TRAY_TRAY_H\n\n#include \"config.h\"\n#if HAVE_LIBSYSTEMD\n#include <systemd/sd-bus.h>\n#elif HAVE_LIBELOGIND\n#include <elogind/sd-bus.h>\n#elif HAVE_BASU\n#include <basu/sd-bus.h>\n#endif\n#include <cairo.h>\n#include <stdint.h>\n#include \"swaybar/tray/host.h\"\n#include \"list.h\"\n\nstruct swaybar;\nstruct swaybar_output;\nstruct swaybar_watcher;\n\nstruct swaybar_tray {\n\tstruct swaybar *bar;\n\n\tint fd;\n\tsd_bus *bus;\n\n\tstruct swaybar_host host_xdg;\n\tstruct swaybar_host host_kde;\n\tlist_t *items; // struct swaybar_sni *\n\tstruct swaybar_watcher *watcher_xdg;\n\tstruct swaybar_watcher *watcher_kde;\n\n\tlist_t *basedirs; // char *\n\tlist_t *themes; // struct swaybar_theme *\n};\n\nstruct swaybar_tray *create_tray(struct swaybar *bar);\nvoid destroy_tray(struct swaybar_tray *tray);\nvoid tray_in(int fd, short mask, void *data);\nuint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x);\n\n#endif\n"
  },
  {
    "path": "include/swaybar/tray/watcher.h",
    "content": "#ifndef _SWAYBAR_TRAY_WATCHER_H\n#define _SWAYBAR_TRAY_WATCHER_H\n\n#include \"swaybar/tray/tray.h\"\n#include \"list.h\"\n\nstruct swaybar_watcher {\n\tchar *interface;\n\tsd_bus *bus;\n\tlist_t *hosts;\n\tlist_t *items;\n\tint version;\n};\n\nstruct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus);\nvoid destroy_watcher(struct swaybar_watcher *watcher);\n\n#endif\n"
  },
  {
    "path": "include/swaynag/config.h",
    "content": "#ifndef _SWAYNAG_CONFIG_H\n#define _SWAYNAG_CONFIG_H\n#include \"swaynag/swaynag.h\"\n#include \"list.h\"\n\nint swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,\n\t\tlist_t *types, struct swaynag_type *type, char **config, bool *debug);\n\nchar *swaynag_get_config_path(void);\n\nint swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types);\n\n#endif\n"
  },
  {
    "path": "include/swaynag/render.h",
    "content": "#ifndef _SWAYNAG_RENDER_H\n#define _SWAYNAG_RENDER_H\n#include \"swaynag/swaynag.h\"\n\nvoid render_frame(struct swaynag *swaynag);\n\n#endif\n"
  },
  {
    "path": "include/swaynag/swaynag.h",
    "content": "#ifndef _SWAYNAG_SWAYNAG_H\n#define _SWAYNAG_SWAYNAG_H\n#include <stdint.h>\n#include <strings.h>\n#include \"list.h\"\n#include \"pool-buffer.h\"\n#include \"cursor-shape-v1-client-protocol.h\"\n\n#include \"swaynag/types.h\"\n\n#define SWAYNAG_MAX_HEIGHT 500\n\nstruct swaynag;\n\nenum swaynag_action_type {\n\tSWAYNAG_ACTION_DISMISS,\n\tSWAYNAG_ACTION_EXPAND,\n\tSWAYNAG_ACTION_COMMAND,\n};\n\nstruct swaynag_pointer {\n\tstruct wl_pointer *pointer;\n\tuint32_t serial;\n\tstruct wl_cursor_theme *cursor_theme;\n\tstruct wl_cursor_image *cursor_image;\n\tstruct wl_surface *cursor_surface;\n\tint x;\n\tint y;\n};\n\nstruct swaynag_seat {\n\tstruct wl_seat *wl_seat;\n\tuint32_t wl_name;\n\tstruct swaynag *swaynag;\n\tstruct swaynag_pointer pointer;\n\tstruct wl_list link;\n};\n\nstruct swaynag_output {\n\tchar *name;\n\tstruct wl_output *wl_output;\n\tuint32_t wl_name;\n\tuint32_t scale;\n\tstruct swaynag *swaynag;\n\tstruct wl_list link;\n};\n\nstruct swaynag_button {\n\tchar *text;\n\tenum swaynag_action_type type;\n\tchar *action;\n\tint x;\n\tint y;\n\tint width;\n\tint height;\n\tbool terminal;\n\tbool dismiss;\n};\n\nstruct swaynag_details {\n\tbool visible;\n\tchar *message;\n\tchar *details_text;\n\n\tint x;\n\tint y;\n\tint width;\n\tint height;\n\n\tint offset;\n\tint visible_lines;\n\tint total_lines;\n\tstruct swaynag_button *button_details;\n\tstruct swaynag_button button_up;\n\tstruct swaynag_button button_down;\n};\n\nstruct swaynag {\n\tbool run_display;\n\n\tstruct wl_display *display;\n\tstruct wl_compositor *compositor;\n\tstruct wl_seat *seat;\n\tstruct wl_shm *shm;\n\tstruct wl_list outputs;  // swaynag_output::link\n\tstruct wl_list seats;  // swaynag_seat::link\n\tstruct swaynag_output *output;\n\tstruct zwlr_layer_shell_v1 *layer_shell;\n\tstruct zwlr_layer_surface_v1 *layer_surface;\n\tstruct wp_cursor_shape_manager_v1 *cursor_shape_manager;\n\tstruct wl_surface *surface;\n\n\tuint32_t width;\n\tuint32_t height;\n\tint32_t scale;\n\tstruct pool_buffer buffers[2];\n\tstruct pool_buffer *current_buffer;\n\n\tstruct swaynag_type *type;\n\tchar *message;\n\tlist_t *buttons;\n\tstruct swaynag_details details;\n};\n\nvoid swaynag_setup(struct swaynag *swaynag);\n\nvoid swaynag_run(struct swaynag *swaynag);\n\nvoid swaynag_destroy(struct swaynag *swaynag);\n\n#endif\n"
  },
  {
    "path": "include/swaynag/types.h",
    "content": "#ifndef _SWAYNAG_TYPES_H\n#define _SWAYNAG_TYPES_H\n\n#include <stdint.h>\n#include <pango/pangocairo.h>\n#include \"list.h\"\n\nstruct swaynag_type {\n\tchar *name;\n\n\tPangoFontDescription *font_description;\n\tchar *output;\n\tuint32_t anchors;\n\tint32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset\n\n\t// Colors\n\tuint32_t button_text;\n\tuint32_t button_background;\n\tuint32_t details_background;\n\tuint32_t background;\n\tuint32_t text;\n\tuint32_t border;\n\tuint32_t border_bottom;\n\n\t// Sizing\n\tssize_t bar_border_thickness;\n\tssize_t message_padding;\n\tssize_t details_border_thickness;\n\tssize_t button_border_thickness;\n\tssize_t button_gap;\n\tssize_t button_gap_close;\n\tssize_t button_margin_right;\n\tssize_t button_padding;\n};\n\nstruct swaynag_type *swaynag_type_new(const char *name);\n\nvoid swaynag_types_add_default(list_t *types);\n\nstruct swaynag_type *swaynag_type_get(list_t *types, char *name);\n\nstruct swaynag_type *swaynag_type_clone(struct swaynag_type *type);\n\nvoid swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src);\n\nvoid swaynag_type_free(struct swaynag_type *type);\n\nvoid swaynag_types_free(list_t *types);\n\n#endif\n"
  },
  {
    "path": "include/util.h",
    "content": "#ifndef _SWAY_UTIL_H\n#define _SWAY_UTIL_H\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <wayland-server-protocol.h>\n\nenum movement_unit {\n\tMOVEMENT_UNIT_PX,\n\tMOVEMENT_UNIT_PPT,\n\tMOVEMENT_UNIT_DEFAULT,\n\tMOVEMENT_UNIT_INVALID,\n};\n\nstruct movement_amount {\n\tint amount;\n\tenum movement_unit unit;\n};\n\n/*\n * Parse units such as \"px\" or \"ppt\"\n */\nenum movement_unit parse_movement_unit(const char *unit);\n\n/*\n * Parse arguments such as \"10\", \"10px\" or \"10 px\".\n * Returns the number of arguments consumed.\n */\nint parse_movement_amount(int argc, char **argv,\n\t\tstruct movement_amount *amount);\n\n/**\n * Wrap i into the range [0, max]\n */\nint wrap(int i, int max);\n\n/**\n * Given a string that represents an RGB(A) color, result will be set to a\n * uint32_t version of the color, as long as it is valid. If it is invalid,\n * then false will be returned and result will be untouched.\n */\nbool parse_color(const char *color, uint32_t *result);\n\nvoid color_to_rgba(float dest[static 4], uint32_t color);\n\n/**\n * Given a string that represents a boolean, return the boolean value. This\n * function also takes in the current boolean value to support toggling. If\n * toggling is not desired, pass in true for current so that toggling values\n * get parsed as not true.\n */\nbool parse_boolean(const char *boolean, bool current);\n\n/**\n * Given a string that represents a floating point value, return a float.\n * Returns NAN on error.\n */\nfloat parse_float(const char *value);\n\nconst char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel);\n\nbool sway_set_cloexec(int fd, bool cloexec);\n\nuint32_t get_current_time_in_msec(void);\n\n#endif\n"
  },
  {
    "path": "meson.build",
    "content": "project(\n\t'sway',\n\t'c',\n\tversion: '1.12-dev',\n\tlicense: 'MIT',\n\tmeson_version: '>=1.3',\n\tdefault_options: [\n\t\t'c_std=c11',\n\t\t'warning_level=2',\n\t\t'werror=true',\n\t\t'wrap_mode=nodownload',\n\t],\n)\n\nadd_project_arguments(\n\t[\n\t\t'-DWLR_USE_UNSTABLE',\n\t\t'-D_POSIX_C_SOURCE=200809L',\n\n\t\t'-Wno-unused-parameter',\n\t\t'-Wno-unused-result',\n\t\t'-Wno-missing-braces',\n\t\t'-Wno-format-zero-length',\n\t\t'-Wundef',\n\t\t'-Wvla',\n\t],\n\tlanguage: 'c',\n)\n\ncc = meson.get_compiler('c')\n\nis_freebsd = host_machine.system().startswith('freebsd')\ndatadir = get_option('datadir')\nsysconfdir = get_option('sysconfdir')\nprefix = get_option('prefix')\n\nif is_freebsd\n\tadd_project_arguments('-D_C11_SOURCE', language: 'c')\nendif\n\n# Execute the wlroots subproject, if any\nwlroots_version = ['>=0.21.0', '<0.22.0']\nsubproject(\n\t'wlroots',\n\tdefault_options: ['examples=false'],\n\trequired: false,\n\tversion: wlroots_version,\n)\nwlroots = dependency('wlroots-0.21', version: wlroots_version, fallback: 'wlroots')\nwlroots_features = {\n\t'xwayland': false,\n\t'libinput_backend': false,\n\t'session': false,\n}\nforeach name, _ : wlroots_features\n\tvar_name = 'have_' + name.underscorify()\n\thave = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'\n\twlroots_features += { name: have }\nendforeach\n\nnull_dep = dependency('', required: false)\n\njsonc = dependency('json-c', version: '>=0.13')\npcre2 = dependency('libpcre2-8')\nwayland_server = dependency('wayland-server', version: '>=1.21.0')\nwayland_client = dependency('wayland-client')\nwayland_cursor = dependency('wayland-cursor')\nwayland_protos = dependency('wayland-protocols', version: '>=1.41', default_options: ['tests=false'])\nxkbcommon = dependency('xkbcommon', version: '>=1.5.0')\ncairo = dependency('cairo')\npango = dependency('pango')\npangocairo = dependency('pangocairo')\ngdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))\npixman = dependency('pixman-1')\nlibevdev = dependency('libevdev')\nlibinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.26.0') : null_dep\nxcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep\ndrm = dependency('libdrm')\nlibudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep\nmath = cc.find_library('m')\nrt = cc.find_library('rt')\nxcb_icccm = wlroots_features['xwayland'] ? dependency('xcb-icccm') : null_dep\nthreads = dependency('threads') # for pthread_setschedparam and pthread_atfork\n\nif get_option('sd-bus-provider') == 'auto'\n\tif not get_option('tray').disabled()\n\t\tassert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto')\n\tendif\n\tsdbus = dependency(['libsystemd', 'libelogind'],\n\t\trequired: false,\n\t\tversion: '>=239',\n\t)\n\tif not sdbus.found()\n\t\tsdbus = dependency('basu', required: false)\n\tendif\nelse\n\tsdbus = dependency(get_option('sd-bus-provider'), required: get_option('tray'))\nendif\n\ntray_deps_found = sdbus.found()\nif get_option('tray').enabled() and not tray_deps_found\n\terror('Building with -Dtray=enabled, but sd-bus has not been not found')\nendif\nhave_tray = (not get_option('tray').disabled()) and tray_deps_found\n\nconf_data = configuration_data()\n\nconf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found())\nconf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd')\nconf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind')\nconf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu')\nconf_data.set10('HAVE_TRAY', have_tray)\nforeach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY', 'LIBINPUT_SWITCH_KEYPAD_SLIDE']\n\tconf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput))\nendforeach\n\nscdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))\nif scdoc.found()\n\tscdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true)\n\tmandir = get_option('mandir')\n\tman_files = [\n\t\t'sway/sway.1.scd',\n\t\t'sway/sway.5.scd',\n\t\t'sway/sway-bar.5.scd',\n\t\t'sway/sway-input.5.scd',\n\t\t'sway/sway-ipc.7.scd',\n\t\t'sway/sway-output.5.scd',\n\t\t'swaybar/swaybar-protocol.7.scd',\n\t\t'swaymsg/swaymsg.1.scd',\n\t]\n\n\tif get_option('swaynag')\n\t\tman_files += [\n\t\t\t'swaynag/swaynag.1.scd',\n\t\t\t'swaynag/swaynag.5.scd',\n\t\t]\n\tendif\n\n\tforeach filename : man_files\n\t\ttopic = filename.split('.')[-3].split('/')[-1]\n\t\tsection = filename.split('.')[-2]\n\t\toutput = '@0@.@1@'.format(topic, section)\n\n\t\tcustom_target(\n\t\t\toutput,\n\t\t\tinput: filename,\n\t\t\toutput: output,\n\t\t\tcommand: scdoc_prog,\n\t\t\tinstall: true,\n\t\t\tfeed: true,\n\t\t\tcapture: true,\n\t\t\tinstall_dir: '@0@/man@1@'.format(mandir, section)\n\t\t)\n\tendforeach\nendif\n\nadd_project_arguments('-DSYSCONFDIR=\"/@0@\"'.format(join_paths(prefix, sysconfdir)), language : 'c')\n\nversion = '\"@0@\"'.format(meson.project_version())\ngit = find_program('git', native: true, required: false)\nif git.found()\n\tgit_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false)\n\tgit_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)\n\tif git_commit.returncode() == 0 and git_branch.returncode() == 0\n\t\tversion = '\"@0@-@1@ (\" __DATE__ \", branch \\'@2@\\')\"'.format(\n\t\t\tmeson.project_version(),\n\t\t\tgit_commit.stdout().strip(),\n\t\t\tgit_branch.stdout().strip(),\n\t\t)\n\tendif\nendif\nadd_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')\n\nfs = import('fs')\n\n# Strip relative path prefixes from the code if possible, otherwise hide them.\nrelative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) + '/'\nif cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=')\n\tadd_project_arguments(\n\t\t'-fmacro-prefix-map=@0@='.format(relative_dir),\n\t\tlanguage: 'c',\n\t)\nelse\n\tadd_project_arguments(\n\t\t'-DSWAY_REL_SRC_DIR=\"@0@\"'.format(relative_dir),\n\t\tlanguage: 'c',\n\t)\nendif\n\n\nsway_inc = include_directories('include')\n\nsubdir('include')\nsubdir('protocols')\nsubdir('common')\nsubdir('sway')\nsubdir('swaymsg')\n\nif get_option('swaybar') or get_option('swaynag')\n\tsubdir('client')\nendif\nif get_option('swaybar')\n\tsubdir('swaybar')\nendif\nif get_option('swaynag')\n\tsubdir('swaynag')\nendif\n\nconfig = configuration_data()\nconfig.set('datadir', join_paths(prefix, datadir))\nconfig.set('prefix', prefix)\nconfig.set('sysconfdir', join_paths(prefix, sysconfdir))\n\nconfigure_file(\n\tconfiguration: config,\n\tinput: 'config.in',\n\toutput: '@BASENAME@',\n\tinstall_dir: join_paths(sysconfdir, 'sway')\n)\n\ninstall_data(\n\t'sway.desktop',\n\tinstall_dir: join_paths(datadir, 'wayland-sessions')\n)\n\nif get_option('default-wallpaper')\n\twallpaper_files = files(\n\t\t'assets/Sway_Wallpaper_Blue_768x1024.png',\n\t\t'assets/Sway_Wallpaper_Blue_768x1024_Portrait.png',\n\t\t'assets/Sway_Wallpaper_Blue_1136x640.png',\n\t\t'assets/Sway_Wallpaper_Blue_1136x640_Portrait.png',\n\t\t'assets/Sway_Wallpaper_Blue_1366x768.png',\n\t\t'assets/Sway_Wallpaper_Blue_1920x1080.png',\n\t\t'assets/Sway_Wallpaper_Blue_2048x1536.png',\n\t\t'assets/Sway_Wallpaper_Blue_2048x1536_Portrait.png',\n\t)\n\twallpaper_install_dir = join_paths(datadir, 'backgrounds', 'sway')\n\n\tinstall_data(wallpaper_files, install_dir: wallpaper_install_dir)\nendif\n\nsubdir('completions')\n\nsummary({\n\t'gdk-pixbuf': gdk_pixbuf.found(),\n\t'tray': have_tray,\n\t'man-pages': scdoc.found(),\n}, bool_yn: true)\n"
  },
  {
    "path": "meson_options.txt",
    "content": "option('default-wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.')\noption('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')\noption('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.')\noption('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.')\noption('swaybar', type: 'boolean', value: true, description: 'Enable support for swaybar')\noption('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag')\noption('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray')\noption('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray')\noption('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')\noption('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library')\n"
  },
  {
    "path": "protocols/meson.build",
    "content": "wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')\n\nwayland_scanner_dep = dependency('wayland-scanner', native: true)\nwayland_scanner = find_program(\n\twayland_scanner_dep.get_variable('wayland_scanner'),\n\tnative: true,\n)\n\nprotocols = [\n\twl_protocol_dir / 'stable/tablet/tablet-v2.xml',\n\twl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',\n\twl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',\n\twl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',\n\t'wlr-layer-shell-unstable-v1.xml',\n\t'wlr-output-power-management-unstable-v1.xml',\n]\n\nwl_protos_src = []\n\nforeach xml : protocols\n\twl_protos_src += custom_target(\n\t\txml.underscorify() + '_c',\n\t\tinput: xml,\n\t\toutput: '@BASENAME@-protocol.c',\n\t\tcommand: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],\n\t)\n\twl_protos_src += custom_target(\n\t\txml.underscorify() + '_server_h',\n\t\tinput: xml,\n\t\toutput: '@BASENAME@-protocol.h',\n\t\tcommand: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],\n\t)\n\twl_protos_src += custom_target(\n\t\txml.underscorify() + '_client_h',\n\t\tinput: xml,\n\t\toutput: '@BASENAME@-client-protocol.h',\n\t\tcommand: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],\n\t)\nendforeach\n"
  },
  {
    "path": "protocols/wlr-layer-shell-unstable-v1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<protocol name=\"wlr_layer_shell_unstable_v1\">\n  <copyright>\n    Copyright © 2017 Drew DeVault\n\n    Permission to use, copy, modify, distribute, and sell this\n    software and its documentation for any purpose is hereby granted\n    without fee, provided that the above copyright notice appear in\n    all copies and that both that copyright notice and this permission\n    notice appear in supporting documentation, and that the name of\n    the copyright holders not be used in advertising or publicity\n    pertaining to distribution of the software without specific,\n    written prior permission.  The copyright holders make no\n    representations about the suitability of this software for any\n    purpose.  It is provided \"as is\" without express or implied\n    warranty.\n\n    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS\n    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\n    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\n    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN\n    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\n    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n    THIS SOFTWARE.\n  </copyright>\n\n  <interface name=\"zwlr_layer_shell_v1\" version=\"5\">\n    <description summary=\"create surfaces that are layers of the desktop\">\n      Clients can use this interface to assign the surface_layer role to\n      wl_surfaces. Such surfaces are assigned to a \"layer\" of the output and\n      rendered with a defined z-depth respective to each other. They may also be\n      anchored to the edges and corners of a screen and specify input handling\n      semantics. This interface should be suitable for the implementation of\n      many desktop shell components, and a broad number of other applications\n      that interact with the desktop.\n    </description>\n\n    <request name=\"get_layer_surface\">\n      <description summary=\"create a layer_surface from a surface\">\n        Create a layer surface for an existing surface. This assigns the role of\n        layer_surface, or raises a protocol error if another role is already\n        assigned.\n\n        Creating a layer surface from a wl_surface which has a buffer attached\n        or committed is a client error, and any attempts by a client to attach\n        or manipulate a buffer prior to the first layer_surface.configure call\n        must also be treated as errors.\n\n        After creating a layer_surface object and setting it up, the client\n        must perform an initial commit without any buffer attached.\n        The compositor will reply with a layer_surface.configure event.\n        The client must acknowledge it and is then allowed to attach a buffer\n        to map the surface.\n\n        You may pass NULL for output to allow the compositor to decide which\n        output to use. Generally this will be the one that the user most\n        recently interacted with.\n\n        Clients can specify a namespace that defines the purpose of the layer\n        surface.\n      </description>\n      <arg name=\"id\" type=\"new_id\" interface=\"zwlr_layer_surface_v1\"/>\n      <arg name=\"surface\" type=\"object\" interface=\"wl_surface\"/>\n      <arg name=\"output\" type=\"object\" interface=\"wl_output\" allow-null=\"true\"/>\n      <arg name=\"layer\" type=\"uint\" enum=\"layer\" summary=\"layer to add this surface to\"/>\n      <arg name=\"namespace\" type=\"string\" summary=\"namespace for the layer surface\"/>\n    </request>\n\n    <enum name=\"error\">\n      <entry name=\"role\" value=\"0\" summary=\"wl_surface has another role\"/>\n      <entry name=\"invalid_layer\" value=\"1\" summary=\"layer value is invalid\"/>\n      <entry name=\"already_constructed\" value=\"2\" summary=\"wl_surface has a buffer attached or committed\"/>\n    </enum>\n\n    <enum name=\"layer\">\n      <description summary=\"available layers for surfaces\">\n        These values indicate which layers a surface can be rendered in. They\n        are ordered by z depth, bottom-most first. Traditional shell surfaces\n        will typically be rendered between the bottom and top layers.\n        Fullscreen shell surfaces are typically rendered at the top layer.\n        Multiple surfaces can share a single layer, and ordering within a\n        single layer is undefined.\n      </description>\n\n      <entry name=\"background\" value=\"0\"/>\n      <entry name=\"bottom\" value=\"1\"/>\n      <entry name=\"top\" value=\"2\"/>\n      <entry name=\"overlay\" value=\"3\"/>\n    </enum>\n\n    <!-- Version 3 additions -->\n\n    <request name=\"destroy\" type=\"destructor\" since=\"3\">\n      <description summary=\"destroy the layer_shell object\">\n        This request indicates that the client will not use the layer_shell\n        object any more. Objects that have been created through this instance\n        are not affected.\n      </description>\n    </request>\n  </interface>\n\n  <interface name=\"zwlr_layer_surface_v1\" version=\"5\">\n    <description summary=\"layer metadata interface\">\n      An interface that may be implemented by a wl_surface, for surfaces that\n      are designed to be rendered as a layer of a stacked desktop-like\n      environment.\n\n      Layer surface state (layer, size, anchor, exclusive zone,\n      margin, interactivity) is double-buffered, and will be applied at the\n      time wl_surface.commit of the corresponding wl_surface is called.\n\n      Attaching a null buffer to a layer surface unmaps it.\n\n      Unmapping a layer_surface means that the surface cannot be shown by the\n      compositor until it is explicitly mapped again. The layer_surface\n      returns to the state it had right after layer_shell.get_layer_surface.\n      The client can re-map the surface by performing a commit without any\n      buffer attached, waiting for a configure event and handling it as usual.\n    </description>\n\n    <request name=\"set_size\">\n      <description summary=\"sets the size of the surface\">\n        Sets the size of the surface in surface-local coordinates. The\n        compositor will display the surface centered with respect to its\n        anchors.\n\n        If you pass 0 for either value, the compositor will assign it and\n        inform you of the assignment in the configure event. You must set your\n        anchor to opposite edges in the dimensions you omit; not doing so is a\n        protocol error. Both values are 0 by default.\n\n        Size is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"width\" type=\"uint\"/>\n      <arg name=\"height\" type=\"uint\"/>\n    </request>\n\n    <request name=\"set_anchor\">\n      <description summary=\"configures the anchor point of the surface\">\n        Requests that the compositor anchor the surface to the specified edges\n        and corners. If two orthogonal edges are specified (e.g. 'top' and\n        'left'), then the anchor point will be the intersection of the edges\n        (e.g. the top left corner of the output); otherwise the anchor point\n        will be centered on that edge, or in the center if none is specified.\n\n        Anchor is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"anchor\" type=\"uint\" enum=\"anchor\"/>\n    </request>\n\n    <request name=\"set_exclusive_zone\">\n      <description summary=\"configures the exclusive geometry of this surface\">\n        Requests that the compositor avoids occluding an area with other\n        surfaces. The compositor's use of this information is\n        implementation-dependent - do not assume that this region will not\n        actually be occluded.\n\n        A positive value is only meaningful if the surface is anchored to one\n        edge or an edge and both perpendicular edges. If the surface is not\n        anchored, anchored to only two perpendicular edges (a corner), anchored\n        to only two parallel edges or anchored to all edges, a positive value\n        will be treated the same as zero.\n\n        A positive zone is the distance from the edge in surface-local\n        coordinates to consider exclusive.\n\n        Surfaces that do not wish to have an exclusive zone may instead specify\n        how they should interact with surfaces that do. If set to zero, the\n        surface indicates that it would like to be moved to avoid occluding\n        surfaces with a positive exclusive zone. If set to -1, the surface\n        indicates that it would not like to be moved to accommodate for other\n        surfaces, and the compositor should extend it all the way to the edges\n        it is anchored to.\n\n        For example, a panel might set its exclusive zone to 10, so that\n        maximized shell surfaces are not shown on top of it. A notification\n        might set its exclusive zone to 0, so that it is moved to avoid\n        occluding the panel, but shell surfaces are shown underneath it. A\n        wallpaper or lock screen might set their exclusive zone to -1, so that\n        they stretch below or over the panel.\n\n        The default value is 0.\n\n        Exclusive zone is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"zone\" type=\"int\"/>\n    </request>\n\n    <request name=\"set_margin\">\n      <description summary=\"sets a margin from the anchor point\">\n        Requests that the surface be placed some distance away from the anchor\n        point on the output, in surface-local coordinates. Setting this value\n        for edges you are not anchored to has no effect.\n\n        The exclusive zone includes the margin.\n\n        Margin is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"top\" type=\"int\"/>\n      <arg name=\"right\" type=\"int\"/>\n      <arg name=\"bottom\" type=\"int\"/>\n      <arg name=\"left\" type=\"int\"/>\n    </request>\n\n    <enum name=\"keyboard_interactivity\">\n      <description summary=\"types of keyboard interaction possible for a layer shell surface\">\n        Types of keyboard interaction possible for layer shell surfaces. The\n        rationale for this is twofold: (1) some applications are not interested\n        in keyboard events and not allowing them to be focused can improve the\n        desktop experience; (2) some applications will want to take exclusive\n        keyboard focus.\n      </description>\n\n      <entry name=\"none\" value=\"0\">\n        <description summary=\"no keyboard focus is possible\">\n          This value indicates that this surface is not interested in keyboard\n          events and the compositor should never assign it the keyboard focus.\n\n          This is the default value, set for newly created layer shell surfaces.\n\n          This is useful for e.g. desktop widgets that display information or\n          only have interaction with non-keyboard input devices.\n        </description>\n      </entry>\n      <entry name=\"exclusive\" value=\"1\">\n        <description summary=\"request exclusive keyboard focus\">\n          Request exclusive keyboard focus if this surface is above the shell surface layer.\n\n          For the top and overlay layers, the seat will always give\n          exclusive keyboard focus to the top-most layer which has keyboard\n          interactivity set to exclusive. If this layer contains multiple\n          surfaces with keyboard interactivity set to exclusive, the compositor\n          determines the one receiving keyboard events in an implementation-\n          defined manner. In this case, no guarantee is made when this surface\n          will receive keyboard focus (if ever).\n\n          For the bottom and background layers, the compositor is allowed to use\n          normal focus semantics.\n\n          This setting is mainly intended for applications that need to ensure\n          they receive all keyboard events, such as a lock screen or a password\n          prompt.\n        </description>\n      </entry>\n      <entry name=\"on_demand\" value=\"2\" since=\"4\">\n        <description summary=\"request regular keyboard focus semantics\">\n          This requests the compositor to allow this surface to be focused and\n          unfocused by the user in an implementation-defined manner. The user\n          should be able to unfocus this surface even regardless of the layer\n          it is on.\n\n          Typically, the compositor will want to use its normal mechanism to\n          manage keyboard focus between layer shell surfaces with this setting\n          and regular toplevels on the desktop layer (e.g. click to focus).\n          Nevertheless, it is possible for a compositor to require a special\n          interaction to focus or unfocus layer shell surfaces (e.g. requiring\n          a click even if focus follows the mouse normally, or providing a\n          keybinding to switch focus between layers).\n\n          This setting is mainly intended for desktop shell components (e.g.\n          panels) that allow keyboard interaction. Using this option can allow\n          implementing a desktop shell that can be fully usable without the\n          mouse.\n        </description>\n      </entry>\n    </enum>\n\n    <request name=\"set_keyboard_interactivity\">\n      <description summary=\"requests keyboard events\">\n        Set how keyboard events are delivered to this surface. By default,\n        layer shell surfaces do not receive keyboard events; this request can\n        be used to change this.\n\n        This setting is inherited by child surfaces set by the get_popup\n        request.\n\n        Layer surfaces receive pointer, touch, and tablet events normally. If\n        you do not want to receive them, set the input region on your surface\n        to an empty region.\n\n        Keyboard interactivity is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"keyboard_interactivity\" type=\"uint\" enum=\"keyboard_interactivity\"/>\n    </request>\n\n    <request name=\"get_popup\">\n      <description summary=\"assign this layer_surface as an xdg_popup parent\">\n        This assigns an xdg_popup's parent to this layer_surface.  This popup\n        should have been created via xdg_surface::get_popup with the parent set\n        to NULL, and this request must be invoked before committing the popup's\n        initial state.\n\n        See the documentation of xdg_popup for more details about what an\n        xdg_popup is and how it is used.\n      </description>\n      <arg name=\"popup\" type=\"object\" interface=\"xdg_popup\"/>\n    </request>\n\n    <request name=\"ack_configure\">\n      <description summary=\"ack a configure event\">\n        When a configure event is received, if a client commits the\n        surface in response to the configure event, then the client\n        must make an ack_configure request sometime before the commit\n        request, passing along the serial of the configure event.\n\n        If the client receives multiple configure events before it\n        can respond to one, it only has to ack the last configure event.\n\n        A client is not required to commit immediately after sending\n        an ack_configure request - it may even ack_configure several times\n        before its next surface commit.\n\n        A client may send multiple ack_configure requests before committing, but\n        only the last request sent before a commit indicates which configure\n        event the client really is responding to.\n      </description>\n      <arg name=\"serial\" type=\"uint\" summary=\"the serial from the configure event\"/>\n    </request>\n\n    <request name=\"destroy\" type=\"destructor\">\n      <description summary=\"destroy the layer_surface\">\n        This request destroys the layer surface.\n      </description>\n    </request>\n\n    <event name=\"configure\">\n      <description summary=\"suggest a surface change\">\n        The configure event asks the client to resize its surface.\n\n        Clients should arrange their surface for the new states, and then send\n        an ack_configure request with the serial sent in this configure event at\n        some point before committing the new surface.\n\n        The client is free to dismiss all but the last configure event it\n        received.\n\n        The width and height arguments specify the size of the window in\n        surface-local coordinates.\n\n        The size is a hint, in the sense that the client is free to ignore it if\n        it doesn't resize, pick a smaller size (to satisfy aspect ratio or\n        resize in steps of NxM pixels). If the client picks a smaller size and\n        is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the\n        surface will be centered on this axis.\n\n        If the width or height arguments are zero, it means the client should\n        decide its own window dimension.\n      </description>\n      <arg name=\"serial\" type=\"uint\"/>\n      <arg name=\"width\" type=\"uint\"/>\n      <arg name=\"height\" type=\"uint\"/>\n    </event>\n\n    <event name=\"closed\">\n      <description summary=\"surface should be closed\">\n        The closed event is sent by the compositor when the surface will no\n        longer be shown. The output may have been destroyed or the user may\n        have asked for it to be removed. Further changes to the surface will be\n        ignored. The client should destroy the resource after receiving this\n        event, and create a new surface if they so choose.\n      </description>\n    </event>\n\n    <enum name=\"error\">\n      <entry name=\"invalid_surface_state\" value=\"0\" summary=\"provided surface state is invalid\"/>\n      <entry name=\"invalid_size\" value=\"1\" summary=\"size is invalid\"/>\n      <entry name=\"invalid_anchor\" value=\"2\" summary=\"anchor bitfield is invalid\"/>\n      <entry name=\"invalid_keyboard_interactivity\" value=\"3\" summary=\"keyboard interactivity is invalid\"/>\n      <entry name=\"invalid_exclusive_edge\" value=\"4\" summary=\"exclusive edge is invalid given the surface anchors\"/>\n    </enum>\n\n    <enum name=\"anchor\" bitfield=\"true\">\n      <entry name=\"top\" value=\"1\" summary=\"the top edge of the anchor rectangle\"/>\n      <entry name=\"bottom\" value=\"2\" summary=\"the bottom edge of the anchor rectangle\"/>\n      <entry name=\"left\" value=\"4\" summary=\"the left edge of the anchor rectangle\"/>\n      <entry name=\"right\" value=\"8\" summary=\"the right edge of the anchor rectangle\"/>\n    </enum>\n\n    <!-- Version 2 additions -->\n\n    <request name=\"set_layer\" since=\"2\">\n      <description summary=\"change the layer of the surface\">\n        Change the layer that the surface is rendered on.\n\n        Layer is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"layer\" type=\"uint\" enum=\"zwlr_layer_shell_v1.layer\" summary=\"layer to move this surface to\"/>\n    </request>\n\n    <!-- Version 5 additions -->\n\n    <request name=\"set_exclusive_edge\" since=\"5\">\n      <description summary=\"set the edge the exclusive zone will be applied to\">\n        Requests an edge for the exclusive zone to apply. The exclusive\n        edge will be automatically deduced from anchor points when possible,\n        but when the surface is anchored to a corner, it will be necessary\n        to set it explicitly to disambiguate, as it is not possible to deduce\n        which one of the two corner edges should be used.\n\n        The edge must be one the surface is anchored to, otherwise the\n        invalid_exclusive_edge protocol error will be raised.\n      </description>\n      <arg name=\"edge\" type=\"uint\" enum=\"anchor\"/>\n    </request>\n  </interface>\n</protocol>\n"
  },
  {
    "path": "protocols/wlr-output-power-management-unstable-v1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<protocol name=\"wlr_output_power_management_unstable_v1\">\n  <copyright>\n    Copyright © 2019 Purism SPC\n\n    Permission is hereby granted, free of charge, to any person obtaining a\n    copy of this software and associated documentation files (the \"Software\"),\n    to deal in the Software without restriction, including without limitation\n    the rights to use, copy, modify, merge, publish, distribute, sublicense,\n    and/or sell copies of the Software, and to permit persons to whom the\n    Software is furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice (including the next\n    paragraph) shall be included in all copies or substantial portions of the\n    Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL\n    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n    DEALINGS IN THE SOFTWARE.\n  </copyright>\n\n  <description summary=\"Control power management modes of outputs\">\n    This protocol allows clients to control power management modes\n    of outputs that are currently part of the compositor space. The\n    intent is to allow special clients like desktop shells to power\n    down outputs when the system is idle.\n\n    To modify outputs not currently part of the compositor space see\n    wlr-output-management.\n\n    Warning! The protocol described in this file is experimental and\n    backward incompatible changes may be made. Backward compatible changes\n    may be added together with the corresponding interface version bump.\n    Backward incompatible changes are done by bumping the version number in\n    the protocol and interface names and resetting the interface version.\n    Once the protocol is to be declared stable, the 'z' prefix and the\n    version number in the protocol and interface names are removed and the\n    interface version number is reset.\n  </description>\n\n  <interface name=\"zwlr_output_power_manager_v1\" version=\"1\">\n    <description summary=\"manager to create per-output power management\">\n      This interface is a manager that allows creating per-output power\n      management mode controls.\n    </description>\n\n    <request name=\"get_output_power\">\n      <description summary=\"get a power management for an output\">\n        Create an output power management mode control that can be used to\n        adjust the power management mode for a given output.\n      </description>\n      <arg name=\"id\" type=\"new_id\" interface=\"zwlr_output_power_v1\"/>\n      <arg name=\"output\" type=\"object\" interface=\"wl_output\"/>\n    </request>\n\n    <request name=\"destroy\" type=\"destructor\">\n      <description summary=\"destroy the manager\">\n        All objects created by the manager will still remain valid, until their\n        appropriate destroy request has been called.\n      </description>\n    </request>\n  </interface>\n\n  <interface name=\"zwlr_output_power_v1\" version=\"1\">\n    <description summary=\"adjust power management mode for an output\">\n      This object offers requests to set the power management mode of\n      an output.\n    </description>\n\n    <enum name=\"mode\">\n      <entry name=\"off\" value=\"0\"\n             summary=\"Output is turned off.\"/>\n      <entry name=\"on\" value=\"1\"\n             summary=\"Output is turned on, no power saving\"/>\n    </enum>\n\n    <enum name=\"error\">\n      <entry name=\"invalid_mode\" value=\"1\" summary=\"nonexistent power save mode\"/>\n    </enum>\n\n    <request name=\"set_mode\">\n      <description summary=\"Set an outputs power save mode\">\n        Set an output's power save mode to the given mode. The mode change\n        is effective immediately. If the output does not support the given\n        mode a failed event is sent.\n      </description>\n      <arg name=\"mode\" type=\"uint\" enum=\"mode\" summary=\"the power save mode to set\"/>\n    </request>\n\n    <event name=\"mode\">\n      <description summary=\"Report a power management mode change\">\n        Report the power management mode change of an output.\n\n        The mode event is sent after an output changed its power\n        management mode. The reason can be a client using set_mode or the\n        compositor deciding to change an output's mode.\n        This event is also sent immediately when the object is created\n        so the client is informed about the current power management mode.\n      </description>\n      <arg name=\"mode\" type=\"uint\" enum=\"mode\"\n           summary=\"the output's new power management mode\"/>\n    </event>\n\n    <event name=\"failed\">\n      <description summary=\"object no longer valid\">\n        This event indicates that the output power management mode control\n        is no longer valid. This can happen for a number of reasons,\n        including:\n        - The output doesn't support power management\n        - Another client already has exclusive power management mode control\n          for this output\n        - The output disappeared\n\n        Upon receiving this event, the client should destroy this object.\n      </description>\n    </event>\n\n    <request name=\"destroy\" type=\"destructor\">\n      <description summary=\"destroy this power management\">\n        Destroys the output power management mode control object.\n      </description>\n    </request>\n  </interface>\n</protocol>\n"
  },
  {
    "path": "release.sh",
    "content": "#!/bin/sh -eu\n\nprev=$(git describe --tags --abbrev=0)\nnext=$(meson rewrite kwargs info project / | jq -r '.kwargs[\"project#/\"].version')\n\ncase \"$next\" in\n*-dev)\n\techo \"This is a development version\"\n\texit 1\n\t;;\nesac\n\nif [ \"$prev\" = \"$next\" ]; then\n\techo \"Version not bumped in meson.build\"\n\texit 1\nfi\n\nif ! git diff-index --quiet HEAD -- meson.build; then\n\techo \"meson.build not committed\"\n\texit 1\nfi\n\nshortlog=\"$(git shortlog --no-merges \"$prev..\")\"\n(echo \"sway $next\"; echo \"\"; echo \"$shortlog\") | git tag \"$next\" -ase -F -\n\nprefix=sway-$next\narchive=$prefix.tar.gz\ngit archive --prefix=\"$prefix/\" -o \"$archive\" \"$next\"\ngpg --output \"$archive\".sig --detach-sig \"$archive\"\n\ngit push --follow-tags\ngh release create \"sway $next\" -t \"$next\" -n \"\" -d \"$archive\" \"$archive.sig\"\n"
  },
  {
    "path": "sway/commands/allow_tearing.c",
    "content": "#include <sway/commands.h>\n#include \"sway/config.h\"\n#include \"sway/tree/view.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_allow_tearing(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"allow_tearing\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container || !container->view) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Tearing can only be allowed on views\");\n\t}\n\n\tbool wants_tearing = parse_boolean(argv[0], true);\n\n\tstruct sway_view *view = container->view;\n\tview->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE :\n\t\tTEARING_OVERRIDE_FALSE;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/assign.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/criteria.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *cmd_assign(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"assign\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\t// Create criteria\n\tchar *err_str = NULL;\n\tstruct criteria *criteria = criteria_parse(argv[0], &err_str);\n\tif (!criteria) {\n\t\terror = cmd_results_new(CMD_INVALID, \"%s\", err_str);\n\t\tfree(err_str);\n\t\treturn error;\n\t}\n\n\t--argc; ++argv;\n\n\tif (has_prefix(*argv, \"→\")) {\n\t\tif (argc < 2) {\n\t\t\tfree(criteria);\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Missing workspace\");\n\t\t}\n\t\t--argc;\n\t\t++argv;\n\t}\n\n\tif (strcmp(*argv, \"output\") == 0) {\n\t\tcriteria->type = CT_ASSIGN_OUTPUT;\n\t\t--argc; ++argv;\n\t} else {\n\t\tif (strcmp(*argv, \"workspace\") == 0) {\n\t\t\t--argc; ++argv;\n\t\t}\n\t\tif (strcmp(*argv, \"number\") == 0) {\n\t\t\t--argc; ++argv;\n\t\t\tif (argv[0][0] < '0' || argv[0][0] > '9') {\n\t\t\t\tfree(criteria);\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Invalid workspace number '%s'\", argv[0]);\n\t\t\t}\n\t\t\tcriteria->type = CT_ASSIGN_WORKSPACE_NUMBER;\n\t\t} else {\n\t\t\tcriteria->type = CT_ASSIGN_WORKSPACE;\n\t\t}\n\t}\n\n\tcriteria->target = join_args(argv, argc);\n\n\tlist_add(config->criteria, criteria);\n\tsway_log(SWAY_DEBUG, \"assign: '%s' -> '%s' added\", criteria->raw,\n\t\t\tcriteria->target);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/bind.c",
    "content": "#include <libevdev/libevdev.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/cursor.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic struct cmd_results *binding_add(struct bar_binding *binding,\n\t\tlist_t *mode_bindings) {\n\tconst char *name = get_mouse_button_name(binding->button);\n\tbool overwritten = false;\n\tfor (int i = 0; i < mode_bindings->length; i++) {\n\t\tstruct bar_binding *other = mode_bindings->items[i];\n\t\tif (other->button == binding->button &&\n\t\t\t\tother->release == binding->release) {\n\t\t\toverwritten = true;\n\t\t\tmode_bindings->items[i] = binding;\n\t\t\tfree_bar_binding(other);\n\t\t\tsway_log(SWAY_DEBUG, \"[bar %s] Updated binding for %u (%s)%s\",\n\t\t\t\t\tconfig->current_bar->id, binding->button, name,\n\t\t\t\t\tbinding->release ? \" - release\" : \"\");\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!overwritten) {\n\t\tlist_add(mode_bindings, binding);\n\t\tsway_log(SWAY_DEBUG, \"[bar %s] Added binding for %u (%s)%s\",\n\t\t\t\tconfig->current_bar->id, binding->button, name,\n\t\t\t\tbinding->release ? \" - release\" : \"\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *binding_remove(struct bar_binding *binding,\n\t\tlist_t *mode_bindings) {\n\tconst char *name = get_mouse_button_name(binding->button);\n\tfor (int i = 0; i < mode_bindings->length; i++) {\n\t\tstruct bar_binding *other = mode_bindings->items[i];\n\t\tif (other->button == binding->button &&\n\t\t\t\tother->release == binding->release) {\n\t\t\tsway_log(SWAY_DEBUG, \"[bar %s] Unbound binding for %u (%s)%s\",\n\t\t\t\t\tconfig->current_bar->id, binding->button, name,\n\t\t\t\t\tbinding->release ? \" - release\" : \"\");\n\t\t\tfree_bar_binding(other);\n\t\t\tfree_bar_binding(binding);\n\t\t\tlist_del(mode_bindings, i);\n\t\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t\t}\n\t}\n\n\tstruct cmd_results *error = cmd_results_new(CMD_FAILURE, \"Could not \"\n\t\t\t\"find binding for [bar %s]\" \" Button %u (%s)%s\",\n\t\t\tconfig->current_bar->id, binding->button, name,\n\t\t\tbinding->release ? \" - release\" : \"\");\n\tfree_bar_binding(binding);\n\treturn error;\n}\n\nstatic struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code,\n\t\tbool unbind) {\n\tint minargs = 2;\n\tconst char *command;\n\tif (unbind) {\n\t\tminargs--;\n\t\tcommand = code ? \"bar unbindcode\" : \"bar unbindsym\";\n\t} else {\n\t\tcommand = code ? \"bar bindcode\" : \"bar bindsym\";\n\t}\n\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, command, EXPECTED_AT_LEAST, minargs))) {\n\t\treturn error;\n\t}\n\n\tstruct bar_binding *binding = calloc(1, sizeof(struct bar_binding));\n\tif (!binding) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate bar binding\");\n\t}\n\n\tbinding->release = false;\n\tif (strcmp(\"--release\", argv[0]) == 0) {\n\t\tbinding->release = true;\n\t\targv++;\n\t\targc--;\n\t}\n\n\tchar *message = NULL;\n\tif (code) {\n\t\tbinding->button = get_mouse_bindcode(argv[0], &message);\n\t} else {\n\t\tbinding->button = get_mouse_bindsym(argv[0], &message);\n\t}\n\tif (message) {\n\t\tfree_bar_binding(binding);\n\t\terror = cmd_results_new(CMD_INVALID, \"%s\", message);\n\t\tfree(message);\n\t\treturn error;\n\t} else if (!binding->button) {\n\t\tfree_bar_binding(binding);\n\t\treturn cmd_results_new(CMD_INVALID, \"Unknown button %s\", argv[0]);\n\t}\n\tlist_t *bindings = config->current_bar->bindings;\n\tif (unbind) {\n\t\treturn binding_remove(binding, bindings);\n\t}\n\n\tbinding->command = join_args(argv + 1, argc - 1);\n\treturn binding_add(binding, bindings);\n}\n\nstruct cmd_results *bar_cmd_bindcode(int argc, char **argv) {\n\treturn bar_cmd_bind(argc, argv, true, false);\n}\n\nstruct cmd_results *bar_cmd_bindsym(int argc, char **argv) {\n\treturn bar_cmd_bind(argc, argv, false, false);\n}\n\nstruct cmd_results *bar_cmd_unbindcode(int argc, char **argv) {\n\treturn bar_cmd_bind(argc, argv, true, true);\n}\n\nstruct cmd_results *bar_cmd_unbindsym(int argc, char **argv) {\n\treturn bar_cmd_bind(argc, argv, false, true);\n}\n"
  },
  {
    "path": "sway/commands/bar/binding_mode_indicator.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc,\n\t\t\t\"binding_mode_indicator\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tconfig->current_bar->binding_mode_indicator =\n\t\tparse_boolean(argv[0], config->current_bar->binding_mode_indicator);\n\tif (config->current_bar->binding_mode_indicator) {\n\t\tsway_log(SWAY_DEBUG, \"Enabling binding mode indicator on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Disabling binding mode indicator on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/colors.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"util.h\"\n\n// Must be in alphabetical order for bsearch\nstatic const struct cmd_handler bar_colors_handlers[] = {\n\t{ \"active_workspace\", bar_colors_cmd_active_workspace },\n\t{ \"background\", bar_colors_cmd_background },\n\t{ \"binding_mode\", bar_colors_cmd_binding_mode },\n\t{ \"focused_background\", bar_colors_cmd_focused_background },\n\t{ \"focused_separator\", bar_colors_cmd_focused_separator },\n\t{ \"focused_statusline\", bar_colors_cmd_focused_statusline },\n\t{ \"focused_workspace\", bar_colors_cmd_focused_workspace },\n\t{ \"inactive_workspace\", bar_colors_cmd_inactive_workspace },\n\t{ \"separator\", bar_colors_cmd_separator },\n\t{ \"statusline\", bar_colors_cmd_statusline },\n\t{ \"urgent_workspace\", bar_colors_cmd_urgent_workspace },\n};\n\nstatic char *hex_to_rgba_hex(const char *hex) {\n\tuint32_t color;\n\tif (!parse_color(hex, &color)) {\n\t\treturn NULL;\n\t}\n\tchar *rgba = malloc(10);\n\tif (!rgba) {\n\t\treturn NULL;\n\t}\n\tsnprintf(rgba, 10, \"#%08x\", color);\n\treturn rgba;\n}\n\nstatic struct cmd_results *parse_single_color(char **color,\n\t\tconst char *cmd_name, int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tchar *rgba = hex_to_rgba_hex(argv[0]);\n\tif (!rgba) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid color: %s\", argv[0]);\n\t}\n\n\tfree(*color);\n\t*color = rgba;\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *parse_three_colors(char ***colors,\n\t\tconst char *cmd_name, int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 3))) {\n\t\treturn error;\n\t}\n\n\tchar *rgba[3] = {0};\n\tfor (int i = 0; i < 3; i++) {\n\t\trgba[i] = hex_to_rgba_hex(argv[i]);\n\t\tif (!rgba[i]) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid color: %s\", argv[i]);\n\t\t}\n\t}\n\n\tfor (int i = 0; i < 3; i++) {\n\t\tfree(*colors[i]);\n\t\t*colors[i] = rgba[i];\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *bar_cmd_colors(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"colors\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\treturn config_subcommand(argv, argc, bar_colors_handlers,\n\t\t\tsizeof(bar_colors_handlers));\n}\n\nstruct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {\n\tchar **colors[3] = {\n\t\t&(config->current_bar->colors.active_workspace_border),\n\t\t&(config->current_bar->colors.active_workspace_bg),\n\t\t&(config->current_bar->colors.active_workspace_text)\n\t};\n\treturn parse_three_colors(colors, \"active_workspace\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_background(int argc, char **argv) {\n\treturn parse_single_color(&(config->current_bar->colors.background),\n\t\t\t\"background\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) {\n\treturn parse_single_color(&(config->current_bar->colors.focused_background),\n\t\t\t\"focused_background\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) {\n\tchar **colors[3] = {\n\t\t&(config->current_bar->colors.binding_mode_border),\n\t\t&(config->current_bar->colors.binding_mode_bg),\n\t\t&(config->current_bar->colors.binding_mode_text)\n\t};\n\treturn parse_three_colors(colors, \"binding_mode\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_focused_workspace(int argc, char **argv) {\n\tchar **colors[3] = {\n\t\t&(config->current_bar->colors.focused_workspace_border),\n\t\t&(config->current_bar->colors.focused_workspace_bg),\n\t\t&(config->current_bar->colors.focused_workspace_text)\n\t};\n\treturn parse_three_colors(colors, \"focused_workspace\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) {\n\tchar **colors[3] = {\n\t\t&(config->current_bar->colors.inactive_workspace_border),\n\t\t&(config->current_bar->colors.inactive_workspace_bg),\n\t\t&(config->current_bar->colors.inactive_workspace_text)\n\t};\n\treturn parse_three_colors(colors, \"inactive_workspace\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_separator(int argc, char **argv) {\n\treturn parse_single_color(&(config->current_bar->colors.separator),\n\t\t\t\"separator\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) {\n\treturn parse_single_color(&(config->current_bar->colors.focused_separator),\n\t\t\t\"focused_separator\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {\n\treturn parse_single_color(&(config->current_bar->colors.statusline),\n\t\t\t\"statusline\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {\n\treturn parse_single_color(&(config->current_bar->colors.focused_statusline),\n\t\t\t\"focused_statusline\", argc, argv);\n}\n\nstruct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) {\n\tchar **colors[3] = {\n\t\t&(config->current_bar->colors.urgent_workspace_border),\n\t\t&(config->current_bar->colors.urgent_workspace_bg),\n\t\t&(config->current_bar->colors.urgent_workspace_text)\n\t};\n\treturn parse_three_colors(colors, \"urgent_workspace\", argc, argv);\n}\n"
  },
  {
    "path": "sway/commands/bar/font.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *bar_cmd_font(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"font\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tchar *font = join_args(argv, argc);\n\tfree(config->current_bar->font);\n\n\tif (has_prefix(font, \"pango:\")) {\n\t\tif (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {\n\t\t\tconfig->current_bar->pango_markup = true;\n\t\t}\n\t\tconfig->current_bar->font = strdup(font + 6);\n\t} else {\n\t\tif (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {\n\t\t\tconfig->current_bar->pango_markup = false;\n\t\t}\n\t\tconfig->current_bar->font = strdup(font);\n\t}\n\n\tfree(font);\n\tsway_log(SWAY_DEBUG, \"Settings font '%s' for bar: %s\",\n\t\t\tconfig->current_bar->font, config->current_bar->id);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/gaps.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/ipc-server.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_gaps(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"gaps\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif ((error = checkarg(argc, \"gaps\", EXPECTED_AT_MOST, 4))) {\n\t\treturn error;\n\t}\n\n\tint top = 0, right = 0, bottom = 0, left = 0;\n\n\tfor (int i = 0; i < argc; i++) {\n\t\tchar *end;\n\t\tint amount = strtol(argv[i], &end, 10);\n\t\tif (strlen(end) && strcasecmp(end, \"px\") != 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Expected 'bar [<bar-id>] gaps <all> | <horizontal> \"\n\t\t\t\t\t\"<vertical> | <top> <right> <bottom> <left>'\");\n\t\t}\n\n\t\tif (i == 0) {\n\t\t\ttop = amount;\n\t\t}\n\t\tif (i == 0 || i == 1) {\n\t\t\tright = amount;\n\t\t}\n\t\tif (i == 0 || i == 2) {\n\t\t\tbottom = amount;\n\t\t}\n\t\tif (i == 0 || i == 1 || i == 3) {\n\t\t\tleft = amount;\n\t\t}\n\t}\n\n\tconfig->current_bar->gaps.top = top;\n\tconfig->current_bar->gaps.right = right;\n\tconfig->current_bar->gaps.bottom = bottom;\n\tconfig->current_bar->gaps.left = left;\n\n\tsway_log(SWAY_DEBUG, \"Setting bar gaps to %d %d %d %d on bar: %s\",\n\t\t\tconfig->current_bar->gaps.top, config->current_bar->gaps.right,\n\t\t\tconfig->current_bar->gaps.bottom, config->current_bar->gaps.left,\n\t\t\tconfig->current_bar->id);\n\n\tif (!config->reading) {\n\t\tipc_event_barconfig_update(config->current_bar);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/height.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_height(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"height\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tint height = atoi(argv[0]);\n\tif (height < 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Invalid height value: %s\", argv[0]);\n\t}\n\tconfig->current_bar->height = height;\n\tsway_log(SWAY_DEBUG, \"Setting bar height to %d on bar: %s\",\n\t\t\theight, config->current_bar->id);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/hidden_state.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/ipc-server.h\"\n#include \"log.h\"\n\nstatic struct cmd_results *bar_set_hidden_state(struct bar_config *bar,\n\t\tconst char *hidden_state) {\n\tchar *old_state = bar->hidden_state;\n\tif (strcasecmp(\"toggle\", hidden_state) == 0 && !config->reading) {\n\t\tif (strcasecmp(\"hide\", bar->hidden_state) == 0) {\n\t\t\tbar->hidden_state = strdup(\"show\");\n\t\t} else if (strcasecmp(\"show\", bar->hidden_state) == 0) {\n\t\t\tbar->hidden_state = strdup(\"hide\");\n\t\t}\n\t} else if (strcasecmp(\"hide\", hidden_state) == 0) {\n\t\tbar->hidden_state = strdup(\"hide\");\n\t} else if (strcasecmp(\"show\", hidden_state) == 0) {\n\t\tbar->hidden_state = strdup(\"show\");\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid value %s\",\thidden_state);\n\t}\n\tif (strcmp(old_state, bar->hidden_state) != 0) {\n\t\tif (!config->current_bar) {\n\t\t\tipc_event_barconfig_update(bar);\n\t\t}\n\t\tsway_log(SWAY_DEBUG, \"Setting hidden_state: '%s' for bar: %s\",\n\t\t\t\tbar->hidden_state, bar->id);\n\t}\n\t// free old mode\n\tfree(old_state);\n\treturn NULL;\n}\n\nstruct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"hidden_state\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif ((error = checkarg(argc, \"hidden_state\", EXPECTED_AT_MOST, 2))) {\n\t\treturn error;\n\t}\n\tif (config->reading && argc > 1) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Unexpected value %s in config mode\", argv[1]);\n\t}\n\n\tif (config->current_bar && argc == 2 &&\n\t\t\tstrcmp(config->current_bar->id, argv[1]) != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Conflicting bar ids: %s and %s\",\n\t\t\t\tconfig->current_bar->id, argv[1]);\n\t}\n\n\tconst char *state = argv[0];\n\tif (config->current_bar) {\n\t\terror = bar_set_hidden_state(config->current_bar, state);\n\t} else {\n\t\tconst char *id = argc == 2 ? argv[1] : NULL;\n\t\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\t\tstruct bar_config *bar = config->bars->items[i];\n\t\t\tif (id) {\n\t\t\t\tif (strcmp(id, bar->id) == 0) {\n\t\t\t\t\terror = bar_set_hidden_state(bar, state);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else if ((error = bar_set_hidden_state(bar, state))) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn error ? error : cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/icon_theme.c",
    "content": "#include <string.h>\n#include \"config.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {\n#if HAVE_TRAY\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"icon_theme\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"[Bar %s] Setting icon theme to %s\",\n\t\t\tconfig->current_bar->id, argv[0]);\n\tfree(config->current_bar->icon_theme);\n\tconfig->current_bar->icon_theme = strdup(argv[0]);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n#else\n\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Sway has been compiled without tray support\");\n#endif\n}\n"
  },
  {
    "path": "sway/commands/bar/id.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_id(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"id\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tconst char *name = argv[0];\n\tconst char *oldname = config->current_bar->id;\n\tif (strcmp(name, oldname) == 0) {\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);  // NOP\n\t} else if (strcmp(name, \"id\") == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"id cannot be 'id'\");\n\t}\n\t// check if id is used by a previously defined bar\n\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\tstruct bar_config *find = config->bars->items[i];\n\t\tif (strcmp(name, find->id) == 0 && config->current_bar != find) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Id '%s' already defined for another bar. Id unchanged (%s).\",\n\t\t\t\t\tname, oldname);\n\t\t}\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Renaming bar: '%s' to '%s'\", oldname, name);\n\n\t// free old bar id\n\tfree(config->current_bar->id);\n\tconfig->current_bar->id = strdup(name);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/mode.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/ipc-server.h\"\n#include \"log.h\"\n\nstatic struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode) {\n\tchar *old_mode = bar->mode;\n\tif (strcasecmp(\"toggle\", mode) == 0 && !config->reading) {\n\t\tif (strcasecmp(\"dock\", bar->mode) == 0) {\n\t\t\tbar->mode = strdup(\"hide\");\n\t\t} else{\n\t\t\tbar->mode = strdup(\"dock\");\n\t\t}\n\t} else if (strcasecmp(\"dock\", mode) == 0) {\n\t\tbar->mode = strdup(\"dock\");\n\t} else if (strcasecmp(\"hide\", mode) == 0) {\n\t\tbar->mode = strdup(\"hide\");\n\t} else if (strcasecmp(\"invisible\", mode) == 0) {\n\t\tbar->mode = strdup(\"invisible\");\n\t} else if (strcasecmp(\"overlay\", mode) == 0) {\n\t\tbar->mode = strdup(\"overlay\");\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid value %s\", mode);\n\t}\n\n\tif (strcmp(old_mode, bar->mode) != 0) {\n\t\tif (!config->current_bar) {\n\t\t\tipc_event_barconfig_update(bar);\n\t\t}\n\t\tsway_log(SWAY_DEBUG, \"Setting mode: '%s' for bar: %s\", bar->mode, bar->id);\n\t}\n\n\t// free old mode\n\tfree(old_mode);\n\treturn NULL;\n}\n\nstruct cmd_results *bar_cmd_mode(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"mode\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif ((error = checkarg(argc, \"mode\", EXPECTED_AT_MOST, 2))) {\n\t\treturn error;\n\t}\n\tif (config->reading && argc > 1) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Unexpected value %s in config mode\", argv[1]);\n\t}\n\n\tif (config->current_bar && argc == 2 &&\n\t\t\tstrcmp(config->current_bar->id, argv[1]) != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Conflicting bar ids: %s and %s\",\n\t\t\t\tconfig->current_bar->id, argv[1]);\n\t}\n\n\tconst char *mode = argv[0];\n\tif (config->current_bar) {\n\t\terror = bar_set_mode(config->current_bar, mode);\n\t} else {\n\t\tconst char *id = argc == 2 ? argv[1] : NULL;\n\t\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\t\tstruct bar_config *bar = config->bars->items[i];\n\t\t\tif (id) {\n\t\t\t\tif (strcmp(id, bar->id) == 0) {\n\t\t\t\t\terror = bar_set_mode(bar, mode);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else if ((error = bar_set_mode(bar, mode))) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn error ? error : cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/modifier.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/input/keyboard.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *bar_cmd_modifier(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"modifier\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tuint32_t mod = 0;\n\tif (strcmp(argv[0], \"none\") != 0) {\n\t\tlist_t *split = split_string(argv[0], \"+\");\n\t\tfor (int i = 0; i < split->length; ++i) {\n\t\t\tuint32_t tmp_mod;\n\t\t\tif ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) {\n\t\t\t\tmod |= tmp_mod;\n\t\t\t} else if (strcmp(split->items[i], \"none\") == 0) {\n\t\t\t\terror = cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"none cannot be used along with other modifiers\");\n\t\t\t\tlist_free_items_and_destroy(split);\n\t\t\t\treturn error;\n\t\t\t} else {\n\t\t\t\terror = cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Unknown modifier '%s'\", (char *)split->items[i]);\n\t\t\t\tlist_free_items_and_destroy(split);\n\t\t\t\treturn error;\n\t\t\t}\n\t\t}\n\t\tlist_free_items_and_destroy(split);\n\t}\n\tconfig->current_bar->modifier = mod;\n\tsway_log(SWAY_DEBUG,\n\t\t\t\"Show/Hide the bar when pressing '%s' in hide mode.\", argv[0]);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/output.c",
    "content": "#include <stdbool.h>\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_output(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"output\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tconst char *output = argv[0];\n\tlist_t *outputs = config->current_bar->outputs;\n\tif (!outputs) {\n\t\toutputs = create_list();\n\t\tconfig->current_bar->outputs = outputs;\n\t}\n\n\tbool add_output = true;\n\tif (strcmp(\"*\", output) == 0) {\n\t\t// remove all previous defined outputs and replace with '*'\n\t\twhile (outputs->length) {\n\t\t\tfree(outputs->items[0]);\n\t\t\tlist_del(outputs, 0);\n\t\t}\n\t} else {\n\t\t// only add output if not already defined, if the list has '*', remove\n\t\t// it, in favor of a manual list\n\t\tfor (int i = 0; i < outputs->length; ++i) {\n\t\t\tconst char *find = outputs->items[i];\n\t\t\tif (strcmp(\"*\", find) == 0) {\n\t\t\t\tfree(outputs->items[i]);\n\t\t\t\tlist_del(outputs, i);\n\t\t\t} else if (strcmp(output, find) == 0) {\n\t\t\t\tadd_output = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (add_output) {\n\t\tlist_add(outputs, strdup(output));\n\t\tsway_log(SWAY_DEBUG, \"Adding bar: '%s' to output '%s'\",\n\t\t\t\tconfig->current_bar->id, output);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/pango_markup.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"pango_markup\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tconfig->current_bar->pango_markup =\n\t\tparse_boolean(argv[0], config->current_bar->pango_markup);\n\tif (config->current_bar->pango_markup) {\n\t\tsway_log(SWAY_DEBUG, \"Enabling pango markup for bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Disabling pango markup for bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/position.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_position(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"position\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tchar *valid[] = { \"top\", \"bottom\" };\n\tfor (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) {\n\t\tif (strcasecmp(valid[i], argv[0]) == 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Setting bar position '%s' for bar: %s\",\n\t\t\t\t\targv[0], config->current_bar->id);\n\t\t\tfree(config->current_bar->position);\n\t\t\tconfig->current_bar->position = strdup(argv[0]);\n\t\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t\t}\n\t}\n\treturn cmd_results_new(CMD_INVALID, \"Invalid value %s\", argv[0]);\n}\n"
  },
  {
    "path": "sway/commands/bar/separator_symbol.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"separator_symbol\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tfree(config->current_bar->separator_symbol);\n\tconfig->current_bar->separator_symbol = strdup(argv[0]);\n\tsway_log(SWAY_DEBUG, \"Settings separator_symbol '%s' for bar: %s\",\n\t\t\tconfig->current_bar->separator_symbol, config->current_bar->id);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/status_command.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *bar_cmd_status_command(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"status_command\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tfree(config->current_bar->status_command);\n\tconfig->current_bar->status_command = NULL;\n\n\tchar *new_command = join_args(argv, argc);\n\tif (strcmp(new_command, \"-\") != 0) {\n\t\tconfig->current_bar->status_command = new_command;\n\t\tsway_log(SWAY_DEBUG, \"Feeding bar with status command: %s\",\n\t\t\t\tconfig->current_bar->status_command);\n\t} else {\n\t\tfree(new_command);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/status_edge_padding.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_status_edge_padding(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"status_edge_padding\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tchar *end;\n\tint padding = strtol(argv[0], &end, 10);\n\tif (strlen(end) || padding < 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Padding must be a positive integer\");\n\t}\n\tconfig->current_bar->status_edge_padding = padding;\n\tsway_log(SWAY_DEBUG, \"Status edge padding on bar %s: %d\",\n\t\t\tconfig->current_bar->id, config->current_bar->status_edge_padding);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/status_padding.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_status_padding(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"status_padding\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tchar *end;\n\tint padding = strtol(argv[0], &end, 10);\n\tif (strlen(end) || padding < 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Padding must be a positive integer\");\n\t}\n\tconfig->current_bar->status_padding = padding;\n\tsway_log(SWAY_DEBUG, \"Status padding on bar %s: %d\",\n\t\t\tconfig->current_bar->id, config->current_bar->status_padding);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/strip_workspace_name.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *bar_cmd_strip_workspace_name(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc,\n\t\t\t\t\"strip_workspace_name\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tconfig->current_bar->strip_workspace_name =\n\t\tparse_boolean(argv[0], config->current_bar->strip_workspace_name);\n\n\tif (config->current_bar->strip_workspace_name) {\n\t\tconfig->current_bar->strip_workspace_numbers = false;\n\n\t\tsway_log(SWAY_DEBUG, \"Stripping workspace name on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Enabling workspace name on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/strip_workspace_numbers.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc,\n\t\t\t\t\"strip_workspace_numbers\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tconfig->current_bar->strip_workspace_numbers =\n\t\tparse_boolean(argv[0], config->current_bar->strip_workspace_numbers);\n\n\tif (config->current_bar->strip_workspace_numbers) {\n\t\tconfig->current_bar->strip_workspace_name = false;\n\n\t\tsway_log(SWAY_DEBUG, \"Stripping workspace numbers on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Enabling workspace numbers on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/swaybar_command.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"swaybar_command\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tfree(config->current_bar->swaybar_command);\n\tconfig->current_bar->swaybar_command = join_args(argv, argc);\n\tsway_log(SWAY_DEBUG, \"Using custom swaybar command: %s\",\n\t\t\tconfig->current_bar->swaybar_command);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/tray_bind.c",
    "content": "#include <strings.h>\n#include \"config.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/cursor.h\"\n#include \"log.h\"\n\nstatic struct cmd_results *tray_bind(int argc, char **argv, bool code) {\n#if HAVE_TRAY\n\tconst char *command = code ? \"bar tray_bindcode\" : \"bar tray_bindsym\";\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, command, EXPECTED_EQUAL_TO, 2))) {\n\t\treturn error;\n\t}\n\n\tstruct tray_binding *binding = calloc(1, sizeof(struct tray_binding));\n\tif (!binding) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate tray binding\");\n\t}\n\n\tchar *message = NULL;\n\tif (code) {\n\t\tbinding->button = get_mouse_bindcode(argv[0], &message);\n\t} else {\n\t\tbinding->button = get_mouse_bindsym(argv[0], &message);\n\t}\n\tif (message) {\n\t\tfree(binding);\n\t\terror = cmd_results_new(CMD_INVALID, \"%s\", message);\n\t\tfree(message);\n\t\treturn error;\n\t} else if (!binding->button) {\n\t\tfree(binding);\n\t\treturn cmd_results_new(CMD_INVALID, \"Unknown button %s\", argv[0]);\n\t}\n\tconst char *name = get_mouse_button_name(binding->button);\n\n\tstatic const char *commands[] = {\n\t\t\"ContextMenu\",\n\t\t\"Activate\",\n\t\t\"SecondaryActivate\",\n\t\t\"ScrollDown\",\n\t\t\"ScrollLeft\",\n\t\t\"ScrollRight\",\n\t\t\"ScrollUp\",\n\t\t\"nop\"\n\t};\n\n\tfor (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i) {\n\t\tif (strcasecmp(argv[1], commands[i]) == 0) {\n\t\t\tbinding->command = commands[i];\n\t\t}\n\t}\n\tif (!binding->command) {\n\t\tfree(binding);\n\t\treturn cmd_results_new(CMD_INVALID, \"[Bar %s] Invalid tray command %s\",\n\t\t\t\tconfig->current_bar->id, argv[1]);\n\t}\n\n\tbool overwritten = false;\n\tstruct tray_binding *other = NULL;\n\twl_list_for_each(other, &config->current_bar->tray_bindings, link) {\n\t\tif (other->button == binding->button) {\n\t\t\toverwritten = true;\n\t\t\tother->command = binding->command;\n\t\t\tfree(binding);\n\t\t\tbinding = other;\n\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\"[bar %s] Updated tray binding for %u (%s) to %s\",\n\t\t\t\t\tconfig->current_bar->id, binding->button, name,\n\t\t\t\t\tbinding->command);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!overwritten) {\n\t\twl_list_insert(&config->current_bar->tray_bindings, &binding->link);\n\t\tsway_log(SWAY_DEBUG, \"[bar %s] Added tray binding for %u (%s) to %s\",\n\t\t\t\tconfig->current_bar->id, binding->button, name,\n\t\t\t\tbinding->command);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n#else\n\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Sway has been compiled without tray support\");\n#endif\n}\n\nstruct cmd_results *bar_cmd_tray_bindcode(int argc, char **argv) {\n\treturn tray_bind(argc, argv, true);\n}\n\nstruct cmd_results *bar_cmd_tray_bindsym(int argc, char **argv) {\n\treturn tray_bind(argc, argv, false);\n}\n"
  },
  {
    "path": "sway/commands/bar/tray_output.c",
    "content": "#include <string.h>\n#include \"config.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_tray_output(int argc, char **argv) {\n#if HAVE_TRAY\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"tray_output\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tlist_t *outputs = config->current_bar->tray_outputs;\n\tif (!outputs) {\n\t\tconfig->current_bar->tray_outputs = outputs = create_list();\n\t}\n\n\tif (strcmp(argv[0], \"none\") == 0) {\n\t\tsway_log(SWAY_DEBUG, \"Hiding tray on bar: %s\", config->current_bar->id);\n\t\tfor (int i = 0; i < outputs->length; ++i) {\n\t\t\tfree(outputs->items[i]);\n\t\t}\n\t\toutputs->length = 0;\n\t} else if (strcmp(argv[0], \"*\") == 0) {\n\t\tsway_log(SWAY_DEBUG, \"Showing tray on all outputs for bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t\twhile (outputs->length) {\n\t\t\tfree(outputs->items[0]);\n\t\t\tlist_del(outputs, 0);\n\t\t}\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Showing tray on output '%s' for bar: %s\", argv[0],\n\t\t\t\tconfig->current_bar->id);\n\t\tif (outputs->length == 1 && strcmp(outputs->items[0], \"none\") == 0) {\n\t\t\tfree(outputs->items[0]);\n\t\t\tlist_del(outputs, 0);\n\t\t}\n\t}\n\tlist_add(outputs, strdup(argv[0]));\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n#else\n\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Sway has been compiled without tray support\");\n#endif\n}\n"
  },
  {
    "path": "sway/commands/bar/tray_padding.c",
    "content": "#include <stdlib.h>\n#include <strings.h>\n#include \"config.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {\n#if HAVE_TRAY\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"tray_padding\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif ((error = checkarg(argc, \"tray_padding\", EXPECTED_AT_MOST, 2))) {\n\t\treturn error;\n\t}\n\n\tstruct bar_config *bar = config->current_bar;\n\n\tchar *end;\n\tint padding = strtol(argv[0], &end, 10);\n\tif (padding < 0 || (*end != '\\0' && strcasecmp(end, \"px\") != 0)) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"[Bar %s] Invalid tray padding value: %s\", bar->id, argv[0]);\n\t}\n\n\tif (argc == 2 && strcasecmp(argv[1], \"px\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'tray_padding <px> [px]'\");\n\t}\n\n\tsway_log(SWAY_DEBUG, \"[Bar %s] Setting tray padding to %d\", bar->id, padding);\n\tconfig->current_bar->tray_padding = padding;\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n#else\n\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Sway has been compiled without tray support\");\n#endif\n}\n"
  },
  {
    "path": "sway/commands/bar/workspace_buttons.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"workspace_buttons\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tconfig->current_bar->workspace_buttons =\n\t\tparse_boolean(argv[0], config->current_bar->workspace_buttons);\n\tif (config->current_bar->workspace_buttons) {\n\t\tsway_log(SWAY_DEBUG, \"Enabling workspace buttons on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Disabling workspace buttons on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/workspace_min_width.c",
    "content": "#include <stdlib.h>\n#include <strings.h>\n#include \"config.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n\nstruct cmd_results *bar_cmd_workspace_min_width(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"workspace_min_width\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tstruct bar_config *bar = config->current_bar;\n\n\tchar *end;\n\tint min_width = strtol(argv[0], &end, 10);\n\tif (min_width < 0 || (*end != '\\0' && strcasecmp(end, \"px\") != 0)) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"[Bar %s] Invalid minimum workspace button width value: %s\",\n\t\t\t\tbar->id, argv[0]);\n\t}\n\n\tif (argc == 2 && strcasecmp(argv[1], \"px\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'workspace_min_width <px> [px]'\");\n\t}\n\n\tsway_log(SWAY_DEBUG, \"[Bar %s] Setting minimum workspace button width to %d\",\n\t\t\tbar->id, min_width);\n\tconfig->current_bar->workspace_min_width = min_width;\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar/wrap_scroll.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"wrap_scroll\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tconfig->current_bar->wrap_scroll =\n\t\t\tparse_boolean(argv[0], config->current_bar->wrap_scroll);\n\tif (config->current_bar->wrap_scroll) {\n\t\tsway_log(SWAY_DEBUG, \"Enabling wrap scroll on bar: %s\",\n\t\t\tconfig->current_bar->id);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Disabling wrap scroll on bar: %s\",\n\t\t\t\tconfig->current_bar->id);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/bar.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/ipc-server.h\"\n#include \"log.h\"\n\n// Must be in alphabetical order for bsearch\nstatic const struct cmd_handler bar_handlers[] = {\n\t{ \"bindcode\", bar_cmd_bindcode },\n\t{ \"binding_mode_indicator\", bar_cmd_binding_mode_indicator },\n\t{ \"bindsym\", bar_cmd_bindsym },\n\t{ \"colors\", bar_cmd_colors },\n\t{ \"font\", bar_cmd_font },\n\t{ \"gaps\", bar_cmd_gaps },\n\t{ \"height\", bar_cmd_height },\n\t{ \"hidden_state\", bar_cmd_hidden_state },\n\t{ \"icon_theme\", bar_cmd_icon_theme },\n\t{ \"mode\", bar_cmd_mode },\n\t{ \"modifier\", bar_cmd_modifier },\n\t{ \"output\", bar_cmd_output },\n\t{ \"pango_markup\", bar_cmd_pango_markup },\n\t{ \"position\", bar_cmd_position },\n\t{ \"separator_symbol\", bar_cmd_separator_symbol },\n\t{ \"status_command\", bar_cmd_status_command },\n\t{ \"status_edge_padding\", bar_cmd_status_edge_padding },\n\t{ \"status_padding\", bar_cmd_status_padding },\n\t{ \"strip_workspace_name\", bar_cmd_strip_workspace_name },\n\t{ \"strip_workspace_numbers\", bar_cmd_strip_workspace_numbers },\n\t{ \"tray_bindcode\", bar_cmd_tray_bindcode },\n\t{ \"tray_bindsym\", bar_cmd_tray_bindsym },\n\t{ \"tray_output\", bar_cmd_tray_output },\n\t{ \"tray_padding\", bar_cmd_tray_padding },\n\t{ \"unbindcode\", bar_cmd_unbindcode },\n\t{ \"unbindsym\", bar_cmd_unbindsym },\n\t{ \"workspace_buttons\", bar_cmd_workspace_buttons },\n\t{ \"workspace_min_width\", bar_cmd_workspace_min_width },\n\t{ \"wrap_scroll\", bar_cmd_wrap_scroll },\n};\n\n// Must be in alphabetical order for bsearch\nstatic const struct cmd_handler bar_config_handlers[] = {\n\t{ \"id\", bar_cmd_id },\n\t{ \"swaybar_command\", bar_cmd_swaybar_command },\n};\n\n// Determines whether the subcommand is valid in any bar handler struct\nstatic bool is_subcommand(char *name) {\n\treturn find_handler(name, bar_handlers, sizeof(bar_handlers)) ||\n\t\tfind_handler(name, bar_config_handlers, sizeof(bar_config_handlers));\n}\n\nstruct cmd_results *cmd_bar(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"bar\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tchar *id = NULL;\n\tif (strcmp(argv[0], \"id\") != 0 && is_subcommand(argv[1])) {\n\t\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\t\tstruct bar_config *item = config->bars->items[i];\n\t\t\tif (strcmp(item->id, argv[0]) == 0) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Selecting bar: %s\", argv[0]);\n\t\t\t\tconfig->current_bar = item;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!config->current_bar) {\n\t\t\tid = strdup(argv[0]);\n\t\t}\n\t\t++argv; --argc;\n\t} else if (config->reading && !config->current_bar) {\n\t\tid = format_str(\"bar-%d\", config->bars->length);\n\t\tif (!id) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate bar id\");\n\t\t}\n\t} else if (!config->reading && strcmp(argv[0], \"mode\") != 0 &&\n\t\t\tstrcmp(argv[0], \"hidden_state\") != 0) {\n\t\tif (is_subcommand(argv[0])) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"No bar defined.\");\n\t\t} else {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Unknown/invalid command '%s'\", argv[1]);\n\t\t}\n\t}\n\n\tif (id) {\n\t\tsway_log(SWAY_DEBUG, \"Creating bar: %s\", id);\n\t\tconfig->current_bar = default_bar_config();\n\t\tif (!config->current_bar) {\n\t\t\tfree(id);\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate bar config\");\n\t\t}\n\t\tconfig->current_bar->id = id;\n\t}\n\n\tstruct cmd_results *res = NULL;\n\tif (find_handler(argv[0], bar_config_handlers,\n\t\t\t\tsizeof(bar_config_handlers))) {\n\t\tif (config->reading) {\n\t\t\tres = config_subcommand(argv, argc, bar_config_handlers,\n\t\t\t\t\tsizeof(bar_config_handlers));\n\t\t} else {\n\t\t\tres = cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Can only be used in the config file\");\n\t\t}\n\t} else {\n\t\tres = config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers));\n\t}\n\n\tif (res && res->status != CMD_SUCCESS) {\n\t\tif (id) {\n\t\t\tfree_bar_config(config->current_bar);\n\t\t\tconfig->current_bar = NULL;\n\t\t\tid = NULL;\n\t\t}\n\t\treturn res;\n\t}\n\n\tif (id) {\n\t\tlist_add(config->bars, config->current_bar);\n\t}\n\n\tif (!config->reading && config->current_bar) {\n\t\tipc_event_barconfig_update(config->current_bar);\n\t\tif (id) {\n\t\t\tload_swaybar(config->current_bar);\n\t\t}\n\t\tconfig->current_bar = NULL;\n\t}\n\n\treturn res;\n}\n"
  },
  {
    "path": "sway/commands/bind.c",
    "content": "#include <libevdev/libevdev.h>\n#include <linux/input-event-codes.h>\n#include <string.h>\n#include <strings.h>\n#include <xkbcommon/xkbcommon.h>\n#include <xkbcommon/xkbcommon-names.h>\n#include <wlr/types/wlr_cursor.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/ipc-server.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nint binding_order = 0;\n\nvoid free_sway_binding(struct sway_binding *binding) {\n\tif (!binding) {\n\t\treturn;\n\t}\n\n\tlist_free_items_and_destroy(binding->keys);\n\tlist_free_items_and_destroy(binding->syms);\n\tfree(binding->input);\n\tfree(binding->command);\n\tfree(binding);\n}\n\nvoid free_switch_binding(struct sway_switch_binding *binding) {\n\tif (!binding) {\n\t\treturn;\n\t}\n\tfree(binding->command);\n\tfree(binding);\n}\n\n/**\n * Returns true if the bindings have the same switch type and state combinations.\n */\nstatic bool binding_switch_compare(struct sway_switch_binding *binding_a,\n\t\tstruct sway_switch_binding *binding_b) {\n\tif (binding_a->type != binding_b->type) {\n\t\treturn false;\n\t}\n\tif (binding_a->trigger != binding_b->trigger) {\n\t\treturn false;\n\t}\n\tif ((binding_a->flags & BINDING_LOCKED) !=\n\t\t\t(binding_b->flags & BINDING_LOCKED)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/**\n * Returns true if the bindings have the same key and modifier combinations.\n * Note that keyboard layout is not considered, so the bindings might actually\n * not be equivalent on some layouts.\n */\nstatic bool binding_key_compare(struct sway_binding *binding_a,\n\t\tstruct sway_binding *binding_b) {\n\tif (strcmp(binding_a->input, binding_b->input) != 0) {\n\t\treturn false;\n\t}\n\n\tif (binding_a->type != binding_b->type) {\n\t\treturn false;\n\t}\n\n\tuint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER\n\t\t\t| BINDING_CONTENTS | BINDING_TITLEBAR | BINDING_LOCKED\n\t\t\t| BINDING_INHIBITED;\n\tif ((binding_a->flags & conflict_generating_flags) !=\n\t\t\t(binding_b->flags & conflict_generating_flags)) {\n\t\treturn false;\n\t}\n\n\tif (binding_a->group != binding_b->group) {\n\t\treturn false;\n\t}\n\n\tif (binding_a->modifiers ^ binding_b->modifiers) {\n\t\treturn false;\n\t}\n\n\tif (binding_a->keys->length != binding_b->keys->length) {\n\t\treturn false;\n\t}\n\n\t// Keys are sorted\n\tint keys_len = binding_a->keys->length;\n\tfor (int i = 0; i < keys_len; ++i) {\n\t\tuint32_t key_a = *(uint32_t *)binding_a->keys->items[i];\n\t\tuint32_t key_b = *(uint32_t *)binding_b->keys->items[i];\n\t\tif (key_a != key_b) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {\n\tuint32_t key_a = **(uint32_t **)keyp_a;\n\tuint32_t key_b = **(uint32_t **)keyp_b;\n\treturn (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0);\n}\n\n/**\n * From a keycode, bindcode, or bindsym name and the most likely binding type,\n * identify the appropriate numeric value corresponding to the key. Return NULL\n * and set *key_val if successful, otherwise return a specific error. Change\n * the value of *type if the initial type guess was incorrect and if this\n * was the first identified key.\n */\nstatic struct cmd_results *identify_key(const char* name, bool first_key,\n\t\tuint32_t* key_val, enum binding_input_type* type) {\n\tif (*type == BINDING_MOUSECODE) {\n\t\t// check for mouse bindcodes\n\t\tchar *message = NULL;\n\t\tuint32_t button = get_mouse_bindcode(name, &message);\n\t\tif (!button) {\n\t\t\tif (message) {\n\t\t\t\tstruct cmd_results *error =\n\t\t\t\t\tcmd_results_new(CMD_INVALID, \"%s\", message);\n\t\t\t\tfree(message);\n\t\t\t\treturn error;\n\t\t\t} else {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Unknown button code %s\", name);\n\t\t\t}\n\t\t}\n\t\t*key_val = button;\n\t} else if (*type == BINDING_MOUSESYM) {\n\t\t// check for mouse bindsyms (x11 buttons or event names)\n\t\tchar *message = NULL;\n\t\tuint32_t button = get_mouse_bindsym(name, &message);\n\t\tif (!button) {\n\t\t\tif (message) {\n\t\t\t\tstruct cmd_results *error =\n\t\t\t\t\tcmd_results_new(CMD_INVALID, \"%s\", message);\n\t\t\t\tfree(message);\n\t\t\t\treturn error;\n\t\t\t} else {\n\t\t\t\treturn cmd_results_new(CMD_INVALID, \"Unknown button %s\", name);\n\t\t\t}\n\t\t}\n\t\t*key_val = button;\n\t} else if (*type == BINDING_KEYCODE) {\n\t\t// check for keycode. If it is the first key, allow mouse bindcodes\n\t\tif (first_key) {\n\t\t\tchar *message = NULL;\n\t\t\tuint32_t button = get_mouse_bindcode(name, &message);\n\t\t\tfree(message);\n\t\t\tif (button) {\n\t\t\t\t*type = BINDING_MOUSECODE;\n\t\t\t\t*key_val = button;\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\n\t\txkb_keycode_t keycode = strtol(name, NULL, 10);\n\t\tif (!xkb_keycode_is_legal_ext(keycode)) {\n\t\t\tif (first_key) {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Invalid keycode or button code '%s'\", name);\n\t\t\t} else {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Invalid keycode '%s'\", name);\n\t\t\t}\n\t\t}\n\t\t*key_val = keycode;\n\t} else {\n\t\t// check for keysym. If it is the first key, allow mouse bindsyms\n\t\tif (first_key) {\n\t\t\tchar *message = NULL;\n\t\t\tuint32_t button = get_mouse_bindsym(name, &message);\n\t\t\tif (message) {\n\t\t\t\tstruct cmd_results *error =\n\t\t\t\t\tcmd_results_new(CMD_INVALID, \"%s\", message);\n\t\t\t\tfree(message);\n\t\t\t\treturn error;\n\t\t\t} else if (button) {\n\t\t\t\t*type = BINDING_MOUSESYM;\n\t\t\t\t*key_val = button;\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\n\t\txkb_keysym_t keysym = xkb_keysym_from_name(name,\n\t\t\t\tXKB_KEYSYM_CASE_INSENSITIVE);\n\t\tif (!keysym) {\n\t\t\tif (first_key) {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Unknown key or button '%s'\", name);\n\t\t\t} else {\n\t\t\t\treturn cmd_results_new(CMD_INVALID, \"Unknown key '%s'\", name);\n\t\t\t}\n\t\t}\n\t\t*key_val = keysym;\n\t}\n\treturn NULL;\n}\n\nstatic struct cmd_results *switch_binding_add(\n\t\tstruct sway_switch_binding *binding, const char *bindtype,\n\t\tconst char *switchcombo, bool warn) {\n\tlist_t *mode_bindings = config->current_mode->switch_bindings;\n\t// overwrite the binding if it already exists\n\tbool overwritten = false;\n\tfor (int i = 0; i < mode_bindings->length; ++i) {\n\t\tstruct sway_switch_binding *config_binding = mode_bindings->items[i];\n\t\tif (binding_switch_compare(binding, config_binding)) {\n\t\t\tsway_log(SWAY_INFO, \"Overwriting binding '%s' to `%s` from `%s`\",\n\t\t\t\t\tswitchcombo, binding->command, config_binding->command);\n\t\t\tif (warn) {\n\t\t\t\tconfig_add_swaynag_warning(\"Overwriting binding\"\n\t\t\t\t\t\t\"'%s' to `%s` from `%s`\",\n\t\t\t\t\t\tswitchcombo, binding->command,\n\t\t\t\t\t\tconfig_binding->command);\n\t\t\t}\n\t\t\tfree_switch_binding(config_binding);\n\t\t\tmode_bindings->items[i] = binding;\n\t\t\toverwritten = true;\n\t\t}\n\t}\n\n\tif (!overwritten) {\n\t\tlist_add(mode_bindings, binding);\n\t\tsway_log(SWAY_DEBUG, \"%s - Bound %s to command `%s`\",\n\t\t\t\tbindtype, switchcombo, binding->command);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *switch_binding_remove(\n\t\tstruct sway_switch_binding *binding, const char *bindtype,\n\t\tconst char *switchcombo) {\n\tlist_t *mode_bindings = config->current_mode->switch_bindings;\n\tfor (int i = 0; i < mode_bindings->length; ++i) {\n\t\tstruct sway_switch_binding *config_binding = mode_bindings->items[i];\n\t\tif (binding_switch_compare(binding, config_binding)) {\n\t\t\tfree_switch_binding(config_binding);\n\t\t\tfree_switch_binding(binding);\n\t\t\tlist_del(mode_bindings, i);\n\t\t\tsway_log(SWAY_DEBUG, \"%s - Unbound %s switch\",\n\t\t\t\t\tbindtype, switchcombo);\n\t\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t\t}\n\t}\n\n\tfree_switch_binding(binding);\n\treturn cmd_results_new(CMD_FAILURE, \"Could not find switch binding `%s`\",\n\t\t\tswitchcombo);\n}\n\n/**\n * Insert or update the binding.\n * Return the binding which has been replaced or NULL.\n */\nstatic struct sway_binding *binding_upsert(struct sway_binding *binding,\n\t\tlist_t *mode_bindings) {\n\tfor (int i = 0; i < mode_bindings->length; ++i) {\n\t\tstruct sway_binding *config_binding = mode_bindings->items[i];\n\t\tif (binding_key_compare(binding, config_binding)) {\n\t\t\tmode_bindings->items[i] = binding;\n\t\t\treturn config_binding;\n\t\t}\n\t}\n\n\tlist_add(mode_bindings, binding);\n\treturn NULL;\n}\n\nstatic struct cmd_results *binding_add(struct sway_binding *binding,\n\t\tlist_t *mode_bindings, const char *bindtype,\n\t\tconst char *keycombo, bool warn) {\n\tstruct sway_binding *config_binding = binding_upsert(binding, mode_bindings);\n\n\tif (config_binding) {\n\t\tsway_log(SWAY_INFO, \"Overwriting binding '%s' for device '%s' \"\n\t\t\t\t\"to `%s` from `%s`\", keycombo, binding->input,\n\t\t\t\tbinding->command, config_binding->command);\n\t\tif (warn) {\n\t\t\tconfig_add_swaynag_warning(\"Overwriting binding\"\n\t\t\t\t\t\"'%s' for device '%s' to `%s` from `%s`\",\n\t\t\t\t\tkeycombo, binding->input, binding->command,\n\t\t\t\t\tconfig_binding->command);\n\t\t}\n\t\tfree_sway_binding(config_binding);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"%s - Bound %s to command `%s` for device '%s'\",\n\t\t\t\tbindtype, keycombo, binding->command, binding->input);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *binding_remove(struct sway_binding *binding,\n\t\tlist_t *mode_bindings, const char *bindtype,\n\t\tconst char *keycombo) {\n\tfor (int i = 0; i < mode_bindings->length; ++i) {\n\t\tstruct sway_binding *config_binding = mode_bindings->items[i];\n\t\tif (binding_key_compare(binding, config_binding)) {\n\t\t\tsway_log(SWAY_DEBUG, \"%s - Unbound `%s` from device '%s'\",\n\t\t\t\t\tbindtype, keycombo, binding->input);\n\t\t\tfree_sway_binding(config_binding);\n\t\t\tfree_sway_binding(binding);\n\t\t\tlist_del(mode_bindings, i);\n\t\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t\t}\n\t}\n\tfree_sway_binding(binding);\n\treturn cmd_results_new(CMD_FAILURE, \"Could not find binding `%s` \"\n\t\t\t\"for the given flags\", keycombo);\n}\n\nstatic struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,\n\t\tbool bindcode, bool unbind) {\n\tconst char *bindtype;\n\tint minargs = 2;\n\tif (unbind) {\n\t\tbindtype = bindcode ? \"unbindcode\" : \"unbindsym\";\n\t\tminargs--;\n\t} else {\n\t\tbindtype = bindcode ? \"bindcode\": \"bindsym\";\n\t}\n\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_binding *binding = calloc(1, sizeof(struct sway_binding));\n\tif (!binding) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate binding\");\n\t}\n\tbinding->input = strdup(\"*\");\n\tbinding->keys = create_list();\n\tbinding->group = XKB_LAYOUT_INVALID;\n\tbinding->modifiers = 0;\n\tbinding->flags = 0;\n\tbinding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM;\n\n\tbool exclude_titlebar = false;\n\tbool warn = true;\n\n\twhile (argc > 0) {\n\t\tif (strcmp(\"--release\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_RELEASE;\n\t\t} else if (strcmp(\"--locked\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_LOCKED;\n\t\t} else if (strcmp(\"--inhibited\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_INHIBITED;\n\t\t} else if (strcmp(\"--whole-window\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR;\n\t\t} else if (strcmp(\"--border\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_BORDER;\n\t\t} else if (strcmp(\"--to-code\", argv[0]) == 0) {\n\t\t\tif (!bindcode) {\n\t\t\t\tbinding->flags |= BINDING_CODE;\n\t\t\t}\n\t\t} else if (strcmp(\"--exclude-titlebar\", argv[0]) == 0) {\n\t\t\texclude_titlebar = true;\n\t\t} else if (has_prefix(argv[0], \"--input-device=\")) {\n\t\t\tfree(binding->input);\n\t\t\tbinding->input = strdup(argv[0] + strlen(\"--input-device=\"));\n\t\t\tstrip_quotes(binding->input);\n\t\t} else if (strcmp(\"--no-warn\", argv[0]) == 0) {\n\t\t\twarn = false;\n\t\t} else if (strcmp(\"--no-repeat\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_NOREPEAT;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t\targv++;\n\t\targc--;\n\t}\n\tif (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR)\n\t\t\t|| exclude_titlebar) {\n\t\tbinding->type = binding->type == BINDING_KEYCODE ?\n\t\t\tBINDING_MOUSECODE : BINDING_MOUSESYM;\n\t}\n\n\tif (argc < minargs) {\n\t\tfree_sway_binding(binding);\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\"Invalid %s command \"\n\t\t\t\"(expected at least %d non-option arguments, got %d)\",\n\t\t\tbindtype, minargs, argc);\n\t}\n\n\tlist_t *split = split_string(argv[0], \"+\");\n\tfor (int i = 0; i < split->length; ++i) {\n\t\t// Check for group\n\t\tif (has_prefix(split->items[i], \"Group\")) {\n\t\t\tif (binding->group != XKB_LAYOUT_INVALID) {\n\t\t\t\tfree_sway_binding(binding);\n\t\t\t\tlist_free_items_and_destroy(split);\n\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\"Only one group can be specified\");\n\t\t\t}\n\t\t\tchar *end;\n\t\t\tint group = strtol(split->items[i] + strlen(\"Group\"), &end, 10);\n\t\t\tif (group < 1 || group > 4 || end[0] != '\\0') {\n\t\t\t\tfree_sway_binding(binding);\n\t\t\t\tlist_free_items_and_destroy(split);\n\t\t\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid group\");\n\t\t\t}\n\t\t\tbinding->group = group - 1;\n\t\t\tcontinue;\n\t\t} else if (strcmp(split->items[i], \"Mode_switch\") == 0) {\n\t\t\t// For full i3 compatibility, Mode_switch is an alias for Group2\n\t\t\tif (binding->group != XKB_LAYOUT_INVALID) {\n\t\t\t\tfree_sway_binding(binding);\n\t\t\t\tlist_free_items_and_destroy(split);\n\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\"Only one group can be specified\");\n\t\t\t}\n\t\t\tbinding->group = 1;\n\t\t}\n\n\t\t// Check for a modifier key\n\t\tuint32_t mod;\n\t\tif ((mod = get_modifier_mask_by_name(split->items[i])) > 0) {\n\t\t\tbinding->modifiers |= mod;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Identify the key and possibly change binding->type\n\t\tuint32_t key_val = 0;\n\t\terror = identify_key(split->items[i], binding->keys->length == 0,\n\t\t\t\t     &key_val, &binding->type);\n\t\tif (error) {\n\t\t\tfree_sway_binding(binding);\n\t\t\tlist_free(split);\n\t\t\treturn error;\n\t\t}\n\n\t\tuint32_t *key = calloc(1, sizeof(uint32_t));\n\t\tif (!key) {\n\t\t\tfree_sway_binding(binding);\n\t\t\tlist_free_items_and_destroy(split);\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Unable to allocate binding key\");\n\t\t}\n\t\t*key = key_val;\n\t\tlist_add(binding->keys, key);\n\t}\n\tlist_free_items_and_destroy(split);\n\n\t// refine region of interest for mouse binding once we are certain\n\t// that this is one\n\tif (exclude_titlebar) {\n\t\tbinding->flags &= ~BINDING_TITLEBAR;\n\t} else if (binding->type == BINDING_MOUSECODE\n\t\t\t|| binding->type == BINDING_MOUSESYM) {\n\t\tbinding->flags |= BINDING_TITLEBAR;\n\t}\n\n\t// sort ascending\n\tlist_qsort(binding->keys, key_qsort_cmp);\n\n\t// translate keysyms into keycodes\n\tif (!translate_binding(binding)) {\n\t\tsway_log(SWAY_INFO,\n\t\t\t\t\"Unable to translate bindsym into bindcode: %s\", argv[0]);\n\t}\n\n\tlist_t *mode_bindings;\n\tif (binding->type == BINDING_KEYCODE) {\n\t\tmode_bindings = config->current_mode->keycode_bindings;\n\t} else if (binding->type == BINDING_KEYSYM) {\n\t\tmode_bindings = config->current_mode->keysym_bindings;\n\t} else {\n\t\tmode_bindings = config->current_mode->mouse_bindings;\n\t}\n\n\tif (unbind) {\n\t\treturn binding_remove(binding, mode_bindings, bindtype, argv[0]);\n\t}\n\n\tbinding->command = join_args(argv + 1, argc - 1);\n\tbinding->order = binding_order++;\n\treturn binding_add(binding, mode_bindings, bindtype, argv[0], warn);\n}\n\nstruct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,\n\t\tbool unbind) {\n\tint minargs = 2;\n\tchar *bindtype = \"bindswitch\";\n\tif (unbind) {\n\t\tminargs--;\n\t\tbindtype = \"unbindswitch\";\n\t}\n\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {\n\t\treturn error;\n\t}\n\tstruct sway_switch_binding *binding = calloc(1, sizeof(struct sway_switch_binding));\n\tif (!binding) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate binding\");\n\t}\n\n\tbool warn = true;\n\n\t// Handle flags\n\twhile (argc > 0) {\n\t\tif (strcmp(\"--locked\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_LOCKED;\n\t\t} else if (strcmp(\"--no-warn\", argv[0]) == 0) {\n\t\t\twarn = false;\n\t\t} else if (strcmp(\"--reload\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_RELOAD;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t\targv++;\n\t\targc--;\n\t}\n\n\tif (argc < minargs) {\n\t\tfree(binding);\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Invalid %s command (expected at least %d \"\n\t\t\t\t\"non-option arguments, got %d)\", bindtype, minargs, argc);\n\t}\n\n\tlist_t *split = split_string(argv[0], \":\");\n\tif (split->length != 2) {\n\t\tfree_switch_binding(binding);\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Invalid %s command (expected binding with the form \"\n\t\t\t\t\"<switch>:<state>)\", bindtype);\n\t}\n\tif (strcmp(split->items[0], \"tablet\") == 0) {\n\t\tbinding->type = WLR_SWITCH_TYPE_TABLET_MODE;\n\t} else if (strcmp(split->items[0], \"lid\") == 0) {\n\t\tbinding->type = WLR_SWITCH_TYPE_LID;\n#if HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE\n\t} else if (strcmp(split->items[0], \"keypad_slide\") == 0) {\n\t\tbinding->type = WLR_SWITCH_TYPE_KEYPAD_SLIDE;\n#endif\n\t} else {\n\t\tfree_switch_binding(binding);\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Invalid %s command (expected switch binding: \"\n\t\t\t\t\"unknown switch %s)\", bindtype,\n\t\t\t\t(const char *)split->items[0]);\n\t}\n\tif (strcmp(split->items[1], \"on\") == 0) {\n\t\tbinding->trigger = SWAY_SWITCH_TRIGGER_ON;\n\t} else if (strcmp(split->items[1], \"off\") == 0) {\n\t\tbinding->trigger = SWAY_SWITCH_TRIGGER_OFF;\n\t} else if (strcmp(split->items[1], \"toggle\") == 0) {\n\t\tbinding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;\n\t} else {\n\t\tfree_switch_binding(binding);\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Invalid %s command \"\n\t\t\t\t\"(expected switch state: unknown state %s)\",\n\t\t\t\tbindtype, (const char *)split->items[1]);\n\t}\n\tlist_free_items_and_destroy(split);\n\n\tif (unbind) {\n\t\treturn switch_binding_remove(binding, bindtype, argv[0]);\n\t}\n\tbinding->command = join_args(argv + 1, argc - 1);\n\treturn switch_binding_add(binding, bindtype, argv[0], warn);\n}\n\nstruct cmd_results *cmd_bindsym(int argc, char **argv) {\n\treturn cmd_bindsym_or_bindcode(argc, argv, false, false);\n}\n\nstruct cmd_results *cmd_bindcode(int argc, char **argv) {\n\treturn cmd_bindsym_or_bindcode(argc, argv, true, false);\n}\n\nstruct cmd_results *cmd_unbindsym(int argc, char **argv) {\n\treturn cmd_bindsym_or_bindcode(argc, argv, false, true);\n}\n\nstruct cmd_results *cmd_unbindcode(int argc, char **argv) {\n\treturn cmd_bindsym_or_bindcode(argc, argv, true, true);\n}\n\nstruct cmd_results *cmd_bindswitch(int argc, char **argv) {\n\treturn cmd_bind_or_unbind_switch(argc, argv, false);\n}\n\nstruct cmd_results *cmd_unbindswitch(int argc, char **argv) {\n\treturn cmd_bind_or_unbind_switch(argc, argv, true);\n}\n\n/**\n * Execute the command associated to a binding\n */\nvoid seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) {\n\tif (!config->active) {\n\t\tsway_log(SWAY_DEBUG, \"deferring command for binding: %s\",\n\t\t\t\tbinding->command);\n\t\tstruct sway_binding *deferred = calloc(1, sizeof(struct sway_binding));\n\t\tif (!deferred) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to allocate deferred binding\");\n\t\t\treturn;\n\t\t}\n\t\tmemcpy(deferred, binding, sizeof(struct sway_binding));\n\t\tdeferred->command = binding->command ? strdup(binding->command) : NULL;\n\t\tlist_add(seat->deferred_bindings, deferred);\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"running command for binding: %s\", binding->command);\n\tstruct sway_container *con = NULL;\n\tif (binding->type == BINDING_MOUSESYM\n\t\t\t|| binding->type == BINDING_MOUSECODE) {\n\t\tstruct wlr_surface *surface = NULL;\n\t\tdouble sx, sy;\n\t\tstruct sway_node *node = node_at_coords(seat,\n\t\t\t\tseat->cursor->cursor->x, seat->cursor->cursor->y,\n\t\t\t\t&surface, &sx, &sy);\n\t\tif (node && node->type == N_CONTAINER) {\n\t\t\tcon = node->sway_container;\n\t\t}\n\t}\n\n\tlist_t *res_list = execute_command(binding->command, seat, con);\n\tbool success = true;\n\tfor (int i = 0; i < res_list->length; ++i) {\n\t\tstruct cmd_results *results = res_list->items[i];\n\t\tif (results->status != CMD_SUCCESS) {\n\t\t\tsway_log(SWAY_DEBUG, \"could not run command for binding: %s (%s)\",\n\t\t\t\tbinding->command, results->error);\n\t\t\tsuccess = false;\n\t\t}\n\t\tfree_cmd_results(results);\n\t}\n\tlist_free(res_list);\n\tif (success) {\n\t\tipc_event_binding(binding);\n\t}\n\n\ttransaction_commit_dirty();\n}\n\n/**\n * The last found keycode associated with the keysym\n * and the total count of matches.\n */\nstruct keycode_matches {\n\txkb_keysym_t keysym;\n\txkb_keycode_t keycode;\n\tint count;\n};\n\n/**\n * Iterate through keycodes in the keymap to find ones matching\n * the specified keysym.\n */\nstatic void find_keycode(struct xkb_keymap *keymap,\n\t\txkb_keycode_t keycode, void *data) {\n\txkb_keysym_t keysym = xkb_state_key_get_one_sym(\n\t\t\tconfig->keysym_translation_state, keycode);\n\n\tif (keysym == XKB_KEY_NoSymbol) {\n\t\treturn;\n\t}\n\n\tstruct keycode_matches *matches = data;\n\tif (matches->keysym == keysym) {\n\t\tmatches->keycode = keycode;\n\t\tmatches->count++;\n\t}\n}\n\n/**\n * Return the keycode for the specified keysym.\n */\nstatic struct keycode_matches get_keycode_for_keysym(xkb_keysym_t keysym) {\n\tstruct keycode_matches matches = {\n\t\t.keysym = keysym,\n\t\t.keycode = XKB_KEYCODE_INVALID,\n\t\t.count = 0,\n\t};\n\n\txkb_keymap_key_for_each(\n\t\t\txkb_state_get_keymap(config->keysym_translation_state),\n\t\t\tfind_keycode, &matches);\n\treturn matches;\n}\n\nbool translate_binding(struct sway_binding *binding) {\n\tif ((binding->flags & BINDING_CODE) == 0) {\n\t\treturn true;\n\t}\n\n\tswitch (binding->type) {\n\t// a bindsym to translate\n\tcase BINDING_KEYSYM:\n\t\tbinding->syms = binding->keys;\n\t\tbinding->keys = create_list();\n\t\tbreak;\n\t// a bindsym to re-translate\n\tcase BINDING_KEYCODE:\n\t\tlist_free_items_and_destroy(binding->keys);\n\t\tbinding->keys = create_list();\n\t\tbreak;\n\tdefault:\n\t\treturn true;\n\t}\n\n\tfor (int i = 0; i < binding->syms->length; ++i) {\n\t\txkb_keysym_t *keysym = binding->syms->items[i];\n\t\tstruct keycode_matches matches = get_keycode_for_keysym(*keysym);\n\n\t\tif (matches.count != 1) {\n\t\t\tsway_log(SWAY_INFO, \"Unable to convert keysym %\" PRIu32 \" into\"\n\t\t\t\t\t\" a single keycode (found %d matches)\",\n\t\t\t\t\t*keysym, matches.count);\n\t\t\tgoto error;\n\t\t}\n\n\t\txkb_keycode_t *keycode = malloc(sizeof(xkb_keycode_t));\n\t\tif (!keycode) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to allocate memory for a keycode\");\n\t\t\tgoto error;\n\t\t}\n\n\t\t*keycode = matches.keycode;\n\t\tlist_add(binding->keys, keycode);\n\t}\n\n\tlist_qsort(binding->keys, key_qsort_cmp);\n\tbinding->type = BINDING_KEYCODE;\n\treturn true;\n\nerror:\n\tlist_free_items_and_destroy(binding->keys);\n\tbinding->type = BINDING_KEYSYM;\n\tbinding->keys = binding->syms;\n\tbinding->syms = NULL;\n\treturn false;\n}\n\nvoid binding_add_translated(struct sway_binding *binding,\n\t\tlist_t *mode_bindings) {\n\tstruct sway_binding *config_binding =\n\t\tbinding_upsert(binding, mode_bindings);\n\n\tif (config_binding) {\n\t\tsway_log(SWAY_INFO, \"Overwriting binding for device '%s' \"\n\t\t\t\t\"to `%s` from `%s`\", binding->input,\n\t\t\t\tbinding->command, config_binding->command);\n\t\tfree_sway_binding(config_binding);\n\t}\n}\n"
  },
  {
    "path": "sway/commands/border.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n\n// A couple of things here:\n// - view->border should never be B_CSD when the view is tiled, even when CSD is\n//   in use (we set using_csd instead and render a sway border).\n// - view->saved_border should be the last applied border when switching to CSD.\n// - view->using_csd should always reflect whether CSD is applied or not.\nstatic void set_border(struct sway_container *con,\n\t\tenum sway_container_border new_border) {\n\tif (con->view) {\n\t\tif (con->view->using_csd && new_border != B_CSD) {\n\t\t\tview_set_csd_from_server(con->view, false);\n\t\t} else if (!con->view->using_csd && new_border == B_CSD) {\n\t\t\tview_set_csd_from_server(con->view, true);\n\t\t\tcon->saved_border = con->pending.border;\n\t\t}\n\t}\n\tif (new_border != B_CSD || container_is_floating(con)) {\n\t\tcon->pending.border = new_border;\n\t}\n\tif (con->view) {\n\t\tcon->view->using_csd = new_border == B_CSD;\n\t}\n}\n\nstatic void border_toggle(struct sway_container *con) {\n\tif (con->view && con->view->using_csd) {\n\t\tset_border(con, B_NONE);\n\t\treturn;\n\t}\n\tswitch (con->pending.border) {\n\tcase B_NONE:\n\t\tset_border(con, B_PIXEL);\n\t\tbreak;\n\tcase B_PIXEL:\n\t\tset_border(con, B_NORMAL);\n\t\tbreak;\n\tcase B_NORMAL:\n\t\tif (con->view && con->view->xdg_decoration) {\n\t\t\tset_border(con, B_CSD);\n\t\t} else {\n\t\t\tset_border(con, B_NONE);\n\t\t}\n\t\tbreak;\n\tcase B_CSD:\n\t\t// view->using_csd should be true so it would have returned above\n\t\tsway_assert(false, \"Unreachable\");\n\t\tbreak;\n\t}\n}\n\nstruct cmd_results *cmd_border(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"border\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container || !container->view) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Only views can have borders\");\n\t}\n\tstruct sway_view *view = container->view;\n\n\tif (strcmp(argv[0], \"none\") == 0) {\n\t\tset_border(container, B_NONE);\n\t} else if (strcmp(argv[0], \"normal\") == 0) {\n\t\tset_border(container, B_NORMAL);\n\t} else if (strcmp(argv[0], \"pixel\") == 0) {\n\t\tset_border(container, B_PIXEL);\n\t} else if (strcmp(argv[0], \"csd\") == 0) {\n\t\tif (!view->xdg_decoration) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"This window doesn't support client side decorations\");\n\t\t}\n\t\tset_border(container, B_CSD);\n\t} else if (strcmp(argv[0], \"toggle\") == 0) {\n\t\tborder_toggle(container);\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'border <none|normal|pixel|csd|toggle>' \"\n\t\t\t\t\"or 'border pixel <px>'\");\n\t}\n\tif (argc == 2) {\n\t\tcontainer->pending.border_thickness = atoi(argv[1]);\n\t}\n\n\tif (container_is_floating(container)) {\n\t\tcontainer_set_geometry_from_content(container);\n\t}\n\n\tarrange_container(container);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/client.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/container.h\"\n#include \"util.h\"\n\nstatic void container_update_iterator(struct sway_container *con, void *data) {\n\tcontainer_update(con);\n}\n\nstatic struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,\n\t\tstruct border_colors *class, const char *default_indicator) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 3)) ||\n\t\t\t(error = checkarg(argc, cmd_name, EXPECTED_AT_MOST, 5))) {\n\t\treturn error;\n\t}\n\n\tif (argc > 3 && strcmp(cmd_name, \"client.focused_tab_title\") == 0) {\n\t\tsway_log(SWAY_ERROR,\n\t\t\t\t\"Warning: indicator and child_border colors have no effect for %s\",\n\t\t\t\tcmd_name);\n\t}\n\n\tstruct border_colors colors = {0};\n\tconst char *ind_hex = argc > 3 ? argv[3] : default_indicator;\n\tconst char *child_hex = argc > 4 ? argv[4] : argv[1];  // def to background\n\n\tstruct {\n\t\tconst char *name;\n\t\tconst char *hex;\n\t\tfloat *rgba[4];\n\t} properties[] = {\n\t\t{ \"border\", argv[0], colors.border },\n\t\t{ \"background\", argv[1], colors.background },\n\t\t{ \"text\", argv[2], colors.text },\n\t\t{ \"indicator\", ind_hex, colors.indicator },\n\t\t{ \"child_border\", child_hex, colors.child_border }\n\t};\n\tfor (size_t i = 0; i < sizeof(properties) / sizeof(properties[0]); i++) {\n\t\tuint32_t color;\n\t\tif (!parse_color(properties[i].hex, &color)) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid %s color %s\",\n\t\t\t\t\tproperties[i].name, properties[i].hex);\n\t\t}\n\t\tcolor_to_rgba(*properties[i].rgba, color);\n\t}\n\n\tmemcpy(class, &colors, sizeof(struct border_colors));\n\n\tif (config->active) {\n\t\troot_for_each_container(container_update_iterator, NULL);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *cmd_client_focused(int argc, char **argv) {\n\treturn handle_command(argc, argv, \"client.focused\",\n\t\t\t&config->border_colors.focused, \"#2e9ef4ff\");\n}\n\nstruct cmd_results *cmd_client_focused_inactive(int argc, char **argv) {\n\treturn handle_command(argc, argv, \"client.focused_inactive\",\n\t\t\t&config->border_colors.focused_inactive, \"#484e50ff\");\n}\n\nstruct cmd_results *cmd_client_unfocused(int argc, char **argv) {\n\treturn handle_command(argc, argv, \"client.unfocused\",\n\t\t\t&config->border_colors.unfocused, \"#292d2eff\");\n}\n\nstruct cmd_results *cmd_client_urgent(int argc, char **argv) {\n\treturn handle_command(argc, argv, \"client.urgent\",\n\t\t\t&config->border_colors.urgent, \"#900000ff\");\n}\n\nstruct cmd_results *cmd_client_noop(int argc, char **argv) {\n\tsway_log(SWAY_INFO, \"Warning: %s is ignored by sway\", argv[-1]);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) {\n\tstruct cmd_results *result =  handle_command(argc, argv,\n\t\t\t\"client.focused_tab_title\",\n\t\t\t&config->border_colors.focused_tab_title, \"#2e9ef4ff\");\n\tif (result && result->status == CMD_SUCCESS) {\n\t\tconfig->has_focused_tab_title = true;\n\t}\n\treturn result;\n}\n"
  },
  {
    "path": "sway/commands/create_output.c",
    "content": "#include <wlr/config.h>\n#include <wlr/backend/headless.h>\n#include <wlr/backend/multi.h>\n#include <wlr/backend/wayland.h>\n#if WLR_HAS_X11_BACKEND\n#include <wlr/backend/x11.h>\n#endif\n#include \"sway/commands.h\"\n#include \"sway/server.h\"\n#include \"log.h\"\n\nstatic void create_output(struct wlr_backend *backend, void *data) {\n\tbool *done = data;\n\tif (*done) {\n\t\treturn;\n\t}\n\n\tif (wlr_backend_is_wl(backend)) {\n\t\twlr_wl_output_create(backend);\n\t\t*done = true;\n\t} else if (wlr_backend_is_headless(backend)) {\n\t\twlr_headless_add_output(backend, 1920, 1080);\n\t\t*done = true;\n\t}\n#if WLR_HAS_X11_BACKEND\n\telse if (wlr_backend_is_x11(backend)) {\n\t\twlr_x11_output_create(backend);\n\t\t*done = true;\n\t}\n#endif\n}\n\n/**\n * This command is intended for developer use only.\n */\nstruct cmd_results *cmd_create_output(int argc, char **argv) {\n\tsway_assert(wlr_backend_is_multi(server.backend),\n\t\t\t\"Expected a multi backend\");\n\n\tbool done = false;\n\twlr_multi_for_each_backend(server.backend, create_output, &done);\n\n\tif (!done) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Can only create outputs for Wayland, X11 or headless backends\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/default_border.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/container.h\"\n\nstruct cmd_results *cmd_default_border(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"default_border\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcmp(argv[0], \"none\") == 0) {\n\t\tconfig->border = B_NONE;\n\t} else if (strcmp(argv[0], \"normal\") == 0) {\n\t\tconfig->border = B_NORMAL;\n\t} else if (strcmp(argv[0], \"pixel\") == 0) {\n\t\tconfig->border = B_PIXEL;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'default_border <none|normal|pixel>' or 'default_border <normal|pixel> <px>'\");\n\t}\n\tif (argc == 2) {\n\t\tconfig->border_thickness = atoi(argv[1]);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/default_floating_border.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/container.h\"\n\nstruct cmd_results *cmd_default_floating_border(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"default_floating_border\",\n\t\t\t\t\tEXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcmp(argv[0], \"none\") == 0) {\n\t\tconfig->floating_border = B_NONE;\n\t} else if (strcmp(argv[0], \"normal\") == 0) {\n\t\tconfig->floating_border = B_NORMAL;\n\t} else if (strcmp(argv[0], \"pixel\") == 0) {\n\t\tconfig->floating_border = B_PIXEL;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'default_floating_border <none|normal|pixel>' \"\n\t\t\t\t\"or 'default_floating_border <normal|pixel> <px>'\");\n\t}\n\tif (argc == 2) {\n\t\tconfig->floating_border_thickness = atoi(argv[1]);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/default_orientation.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n\nstruct cmd_results *cmd_default_orientation(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"default_orientation\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (strcasecmp(argv[0], \"horizontal\") == 0) {\n\t\tconfig->default_orientation = L_HORIZ;\n\t} else if (strcasecmp(argv[0], \"vertical\") == 0) {\n\t\tconfig->default_orientation = L_VERT;\n\t} else if (strcasecmp(argv[0], \"auto\") == 0) {\n\t\t// Do nothing\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'orientation <horizontal|vertical|auto>'\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/exec.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *cmd_exec(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = cmd_exec_validate(argc, argv))) {\n\t\treturn error;\n\t}\n\tif (config->reloading) {\n\t\tchar *args = join_args(argv, argc);\n\t\tsway_log(SWAY_DEBUG, \"Ignoring 'exec %s' due to reload\", args);\n\t\tfree(args);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\treturn cmd_exec_process(argc, argv);\n}\n"
  },
  {
    "path": "sway/commands/exec_always.c",
    "content": "#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <signal.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/server.h\"\n#include \"sway/desktop/launcher.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/workspace.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *cmd_exec_validate(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif (!config->active || config->validating) {\n\t\treturn cmd_results_new(CMD_DEFER, NULL);\n\t}\n\treturn error;\n}\n\nstruct cmd_results *cmd_exec_process(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tchar *cmd = NULL;\n\tbool no_startup_id = false;\n\tif (strcmp(argv[0], \"--no-startup-id\") == 0) {\n\t\tno_startup_id = true;\n\t\t--argc; ++argv;\n\t\tif ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {\n\t\t\treturn error;\n\t\t}\n\t}\n\n\tif (argc == 1 && (argv[0][0] == '\\'' || argv[0][0] == '\"')) {\n\t\tcmd = strdup(argv[0]);\n\t\tstrip_quotes(cmd);\n\t} else {\n\t\tcmd = join_args(argv, argc);\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Executing %s\", cmd);\n\n\tstruct launcher_ctx *ctx = launcher_ctx_create_internal();\n\n\t// Fork process\n\tpid_t child = fork();\n\tif (child == 0) {\n\t\tsetsid();\n\n\t\tif (ctx) {\n\t\t\tconst char *token = launcher_ctx_get_token_name(ctx);\n\t\t\tsetenv(\"XDG_ACTIVATION_TOKEN\", token, 1);\n\t\t\tif (!no_startup_id) {\n\t\t\t\tsetenv(\"DESKTOP_STARTUP_ID\", token, 1);\n\t\t\t}\n\t\t}\n\n\t\texeclp(\"sh\", \"sh\", \"-c\", cmd, (void*)NULL);\n\t\tsway_log_errno(SWAY_ERROR, \"execve failed\");\n\t\t_exit(0); // Close child process\n\t} else if (child < 0) {\n\t\tlauncher_ctx_destroy(ctx);\n\t\tfree(cmd);\n\t\treturn cmd_results_new(CMD_FAILURE, \"fork() failed\");\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Child process created with pid %d\", child);\n\tif (ctx != NULL) {\n\t\tsway_log(SWAY_DEBUG, \"Recording workspace for process %d\", child);\n\t\tctx->pid = child;\n\t}\n\n\tfree(cmd);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *cmd_exec_always(int argc, char **argv) {\n\tstruct cmd_results *error;\n\tif ((error = cmd_exec_validate(argc, argv))) {\n\t\treturn error;\n\t}\n\treturn cmd_exec_process(argc, argv);\n}\n"
  },
  {
    "path": "sway/commands/exit.c",
    "content": "#include <stddef.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/server.h\"\n\nstruct cmd_results *cmd_exit(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"exit\", EXPECTED_EQUAL_TO, 0))) {\n\t\treturn error;\n\t}\n\tsway_terminate(0);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/floating.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"list.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_floating(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"floating\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tstruct sway_container *container = config->handler_context.container;\n\tstruct sway_workspace *workspace = config->handler_context.workspace;\n\tif (!container && workspace->tiling->length == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Can't float an empty workspace\");\n\t}\n\tif (!container) {\n\t\t// Wrap the workspace's children in a container so we can float it\n\t\tcontainer = workspace_wrap_children(workspace);\n\t\tworkspace->layout = L_HORIZ;\n\t\tseat_set_focus_container(config->handler_context.seat, container);\n\t}\n\n\tif (container_is_scratchpad_hidden(container)) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't change floating on hidden scratchpad container\");\n\t}\n\n\t// If the container is in a floating split container,\n\t// operate on the split container instead of the child.\n\tif (container_is_floating_or_child(container)) {\n\t\twhile (container->pending.parent) {\n\t\t\tcontainer = container->pending.parent;\n\t\t}\n\t}\n\n\tbool wants_floating =\n\t\tparse_boolean(argv[0], container_is_floating(container));\n\n\tcontainer_set_floating(container, wants_floating);\n\n\t// Floating containers in the scratchpad should be ignored\n\tif (container->pending.workspace) {\n\t\tarrange_workspace(container->pending.workspace);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/floating_minmax_size.c",
    "content": "#include <errno.h>\n#include <math.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstatic const char min_usage[] =\n\t\"Expected 'floating_minimum_size <width> x <height>'\";\n\nstatic const char max_usage[] =\n\t\"Expected 'floating_maximum_size <width> x <height>'\";\n\nstatic struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,\n\t\tconst char *usage, int *config_width, int *config_height) {\n\tstruct cmd_results *error;\n\tif ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 3))) {\n\t\treturn error;\n\t}\n\n\tchar *err;\n\tint width = (int)strtol(argv[0], &err, 10);\n\tif (*err) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t}\n\n\tif (strcmp(argv[1], \"x\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t}\n\n\tint height = (int)strtol(argv[2], &err, 10);\n\tif (*err) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t}\n\n\t*config_width = width;\n\t*config_height = height;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *cmd_floating_minimum_size(int argc, char **argv) {\n\treturn handle_command(argc, argv, \"floating_minimum_size\", min_usage,\n\t\t\t&config->floating_minimum_width, &config->floating_minimum_height);\n}\n\nstruct cmd_results *cmd_floating_maximum_size(int argc, char **argv) {\n\treturn handle_command(argc, argv, \"floating_maximum_size\", max_usage,\n\t\t\t&config->floating_maximum_width, &config->floating_maximum_height);\n}\n"
  },
  {
    "path": "sway/commands/floating_modifier.c",
    "content": "#include \"strings.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/keyboard.h\"\n\nstruct cmd_results *cmd_floating_modifier(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"floating_modifier\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcasecmp(argv[0], \"none\") == 0) {\n\t\tconfig->floating_mod = 0;\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tuint32_t mod = get_modifier_mask_by_name(argv[0]);\n\tif (!mod) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid modifier\");\n\t}\n\n\tif (argc == 1 || strcasecmp(argv[1], \"normal\") == 0) {\n\t\tconfig->floating_mod_inverse = false;\n\t} else if (strcasecmp(argv[1], \"inverse\") == 0) {\n\t\tconfig->floating_mod_inverse = true;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Usage: floating_modifier <mod> [inverse|normal]\");\n\t}\n\n\tconfig->floating_mod = mod;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/focus.c",
    "content": "#include <float.h>\n#include <strings.h>\n#include <wlr/types/wlr_output_layout.h>\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nstatic bool get_direction_from_next_prev(struct sway_container *container,\n\t\tstruct sway_seat *seat, const char *name, enum wlr_direction *out) {\n\tenum sway_container_layout parent_layout = L_NONE;\n\tif (container) {\n\t\tparent_layout = container_parent_layout(container);\n\t}\n\n\tif (strcasecmp(name, \"prev\") == 0) {\n\t\tswitch (parent_layout) {\n\t\tcase L_HORIZ:\n\t\tcase L_TABBED:\n\t\t\t*out = WLR_DIRECTION_LEFT;\n\t\t\tbreak;\n\t\tcase L_VERT:\n\t\tcase L_STACKED:\n\t\t\t*out = WLR_DIRECTION_UP;\n\t\t\tbreak;\n\t\tcase L_NONE:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t} else if (strcasecmp(name, \"next\") == 0) {\n\t\tswitch (parent_layout) {\n\t\tcase L_HORIZ:\n\t\tcase L_TABBED:\n\t\t\t*out = WLR_DIRECTION_RIGHT;\n\t\t\tbreak;\n\t\tcase L_VERT:\n\t\tcase L_STACKED:\n\t\t\t*out = WLR_DIRECTION_DOWN;\n\t\t\tbreak;\n\t\tcase L_NONE:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic bool parse_direction(const char *name,\n\t\tenum wlr_direction *out) {\n\tif (strcasecmp(name, \"left\") == 0) {\n\t\t*out = WLR_DIRECTION_LEFT;\n\t} else if (strcasecmp(name, \"right\") == 0) {\n\t\t*out = WLR_DIRECTION_RIGHT;\n\t} else if (strcasecmp(name, \"up\") == 0) {\n\t\t*out = WLR_DIRECTION_UP;\n\t} else if (strcasecmp(name, \"down\") == 0) {\n\t\t*out = WLR_DIRECTION_DOWN;\n\t} else {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/**\n * Get node in the direction of newly entered output.\n */\nstatic struct sway_node *get_node_in_output_direction(\n\t\tstruct sway_output *output, enum wlr_direction dir) {\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tstruct sway_workspace *ws = output_get_active_workspace(output);\n\tif (!sway_assert(ws, \"Expected output to have a workspace\")) {\n\t\treturn NULL;\n\t}\n\tif (ws->fullscreen) {\n\t\treturn seat_get_focus_inactive(seat, &ws->fullscreen->node);\n\t}\n\tstruct sway_container *container = NULL;\n\n\tif (ws->tiling->length > 0) {\n\t\tswitch (dir) {\n\t\tcase WLR_DIRECTION_LEFT:\n\t\t\tif (ws->layout == L_HORIZ || ws->layout == L_TABBED) {\n\t\t\t\t// get most right child of new output\n\t\t\t\tcontainer = ws->tiling->items[ws->tiling->length-1];\n\t\t\t} else {\n\t\t\t\tcontainer = seat_get_focus_inactive_tiling(seat, ws);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase WLR_DIRECTION_RIGHT:\n\t\t\tif (ws->layout == L_HORIZ || ws->layout == L_TABBED) {\n\t\t\t\t// get most left child of new output\n\t\t\t\tcontainer = ws->tiling->items[0];\n\t\t\t} else {\n\t\t\t\tcontainer = seat_get_focus_inactive_tiling(seat, ws);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase WLR_DIRECTION_UP:\n\t\t\tif (ws->layout == L_VERT || ws->layout == L_STACKED) {\n\t\t\t\t// get most bottom child of new output\n\t\t\t\tcontainer = ws->tiling->items[ws->tiling->length-1];\n\t\t\t} else {\n\t\t\t\tcontainer = seat_get_focus_inactive_tiling(seat, ws);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase WLR_DIRECTION_DOWN:\n\t\t\tif (ws->layout == L_VERT || ws->layout == L_STACKED) {\n\t\t\t\t// get most top child of new output\n\t\t\t\tcontainer = ws->tiling->items[0];\n\t\t\t} else {\n\t\t\t\tcontainer = seat_get_focus_inactive_tiling(seat, ws);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (container) {\n\t\tcontainer = seat_get_focus_inactive_view(seat, &container->node);\n\t\treturn &container->node;\n\t}\n\n\treturn &ws->node;\n}\n\nstatic struct sway_node *node_get_in_direction_tiling(\n\t\tstruct sway_container *container, struct sway_seat *seat,\n\t\tenum wlr_direction dir, bool descend) {\n\tstruct sway_container *wrap_candidate = NULL;\n\tstruct sway_container *current = container;\n\twhile (current) {\n\t\tif (current->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {\n\t\t\t// Fullscreen container with a direction - go straight to outputs\n\t\t\tstruct sway_output *output = current->pending.workspace->output;\n\t\t\tstruct sway_output *new_output =\n\t\t\t\toutput_get_in_direction(output, dir);\n\t\t\tif (!new_output) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\treturn get_node_in_output_direction(new_output, dir);\n\t\t}\n\t\tif (current->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\tbool can_move = false;\n\t\tint desired;\n\t\tint idx = container_sibling_index(current);\n\t\tenum sway_container_layout parent_layout =\n\t\t\tcontainer_parent_layout(current);\n\t\tlist_t *siblings = container_get_siblings(current);\n\n\t\tif (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT) {\n\t\t\tif (parent_layout == L_HORIZ || parent_layout == L_TABBED) {\n\t\t\t\tcan_move = true;\n\t\t\t\tdesired = idx + (dir == WLR_DIRECTION_LEFT ? -1 : 1);\n\t\t\t}\n\t\t} else {\n\t\t\tif (parent_layout == L_VERT || parent_layout == L_STACKED) {\n\t\t\t\tcan_move = true;\n\t\t\t\tdesired = idx + (dir == WLR_DIRECTION_UP ? -1 : 1);\n\t\t\t}\n\t\t}\n\n\t\tif (can_move) {\n\t\t\tif (desired < 0 || desired >= siblings->length) {\n\t\t\t\tint len = siblings->length;\n\t\t\t\tif (config->focus_wrapping != WRAP_NO && !wrap_candidate\n\t\t\t\t\t\t&& len > 1) {\n\t\t\t\t\tif (desired < 0) {\n\t\t\t\t\t\twrap_candidate = siblings->items[len-1];\n\t\t\t\t\t} else {\n\t\t\t\t\t\twrap_candidate = siblings->items[0];\n\t\t\t\t\t}\n\t\t\t\t\tif (config->focus_wrapping == WRAP_FORCE) {\n\t\t\t\t\t\tstruct sway_container *c = seat_get_focus_inactive_view(\n\t\t\t\t\t\t\t\tseat, &wrap_candidate->node);\n\t\t\t\t\t\treturn &c->node;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tstruct sway_container *desired_con = siblings->items[desired];\n\t\t\t\tif (!descend) {\n\t\t\t\t\treturn &desired_con->node;\n\t\t\t\t} else {\n\t\t\t\t\tstruct sway_container *c = seat_get_focus_inactive_view(\n\t\t\t\t\t\t\tseat, &desired_con->node);\n\t\t\t\t\treturn &c->node;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcurrent = current->pending.parent;\n\t}\n\n\t// Check a different output\n\tstruct sway_output *output = container->pending.workspace->output;\n\tstruct sway_output *new_output = output_get_in_direction(output, dir);\n\tif ((config->focus_wrapping != WRAP_WORKSPACE ||\n\t\t\t\tcontainer->node.type == N_WORKSPACE) && new_output) {\n\t\treturn get_node_in_output_direction(new_output, dir);\n\t}\n\n\t// If there is a wrap candidate, return its focus inactive view\n\tif (wrap_candidate) {\n\t\tstruct sway_container *wrap_inactive = seat_get_focus_inactive_view(\n\t\t\t\tseat, &wrap_candidate->node);\n\t\treturn &wrap_inactive->node;\n\t}\n\n\treturn NULL;\n}\n\nstatic struct sway_node *node_get_in_direction_floating(\n\t\tstruct sway_container *con, struct sway_seat *seat,\n\t\tenum wlr_direction dir) {\n\tdouble ref_lx = con->pending.x + con->pending.width / 2;\n\tdouble ref_ly = con->pending.y + con->pending.height / 2;\n\tdouble closest_distance = DBL_MAX;\n\tstruct sway_container *closest_con = NULL;\n\n\tif (!con->pending.workspace) {\n\t\treturn NULL;\n\t}\n\n\tfor (int i = 0; i < con->pending.workspace->floating->length; i++) {\n\t\tstruct sway_container *floater = con->pending.workspace->floating->items[i];\n\t\tif (floater == con) {\n\t\t\tcontinue;\n\t\t}\n\t\tfloat distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT\n\t\t\t? (floater->pending.x + floater->pending.width / 2) - ref_lx\n\t\t\t: (floater->pending.y + floater->pending.height / 2) - ref_ly;\n\t\tif (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) {\n\t\t\tdistance = -distance;\n\t\t}\n\t\tif (distance < 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (distance < closest_distance) {\n\t\t\tclosest_distance = distance;\n\t\t\tclosest_con = floater;\n\t\t}\n\t}\n\n\treturn closest_con ? &closest_con->node : NULL;\n}\n\nstatic struct cmd_results *focus_mode(struct sway_workspace *ws,\n\t\tstruct sway_seat *seat, bool floating) {\n\tstruct sway_container *new_focus = NULL;\n\tif (floating) {\n\t\tnew_focus = seat_get_focus_inactive_floating(seat, ws);\n\t} else {\n\t\tnew_focus = seat_get_focus_inactive_tiling(seat, ws);\n\t}\n\tif (new_focus) {\n\t\tstruct sway_container *new_focus_view =\n\t\t\tseat_get_focus_inactive_view(seat, &new_focus->node);\n\t\tif (new_focus_view) {\n\t\t\tnew_focus = new_focus_view;\n\t\t}\n\t\tseat_set_focus_container(seat, new_focus);\n\n\t\t// If we're on the floating layer and the floating container area\n\t\t// overlaps the position on the tiling layer that would be warped to,\n\t\t// `seat_consider_warp_to_focus` would decide not to warp, but we need\n\t\t// to anyway.\n\t\tif (config->mouse_warping == WARP_CONTAINER) {\n\t\t\tcursor_warp_to_container(seat->cursor, new_focus, true);\n\t\t} else {\n\t\t\tseat_consider_warp_to_focus(seat);\n\t\t}\n\t} else {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Failed to find a %s container in workspace.\",\n\t\t\t\tfloating ? \"floating\" : \"tiling\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *focus_output(struct sway_seat *seat,\n\t\tint argc, char **argv) {\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'focus output <direction|name>'.\");\n\t}\n\tchar *identifier = join_args(argv, argc);\n\tstruct sway_output *output = output_by_name_or_id(identifier);\n\n\tif (!output) {\n\t\tenum wlr_direction direction;\n\t\tif (!parse_direction(identifier, &direction)) {\n\t\t\tfree(identifier);\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"There is no output with that name.\");\n\t\t}\n\t\tstruct sway_workspace *ws = seat_get_focused_workspace(seat);\n\t\tif (!ws) {\n\t\t\tfree(identifier);\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"No focused workspace to base directions off of.\");\n\t\t}\n\t\toutput = output_get_in_direction(ws->output, direction);\n\n\t\tif (!output) {\n\t\t\tint center_lx = ws->output->lx + ws->output->width / 2;\n\t\t\tint center_ly = ws->output->ly + ws->output->height / 2;\n\t\t\tstruct wlr_output *target = wlr_output_layout_farthest_output(\n\t\t\t\t\troot->output_layout, opposite_direction(direction),\n\t\t\t\t\tws->output->wlr_output, center_lx, center_ly);\n\t\t\tif (target) {\n\t\t\t\toutput = output_from_wlr_output(target);\n\t\t\t}\n\t\t}\n\t}\n\n\tfree(identifier);\n\tif (output) {\n\t\tseat_set_focus(seat, seat_get_focus_inactive(seat, &output->node));\n\t\tseat_consider_warp_to_focus(seat);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *focus_parent(void) {\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tstruct sway_container *con = config->handler_context.container;\n\tif (!con || con->pending.fullscreen_mode) {\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\tstruct sway_node *parent = node_get_parent(&con->node);\n\tif (parent) {\n\t\tseat_set_focus(seat, parent);\n\t\tseat_consider_warp_to_focus(seat);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *focus_child(void) {\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tstruct sway_node *node = config->handler_context.node;\n\tstruct sway_node *focus = seat_get_active_tiling_child(seat, node);\n\tif (focus) {\n\t\tseat_set_focus(seat, focus);\n\t\tseat_consider_warp_to_focus(seat);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *cmd_focus(int argc, char **argv) {\n\tif (config->reading || !config->active) {\n\t\treturn cmd_results_new(CMD_DEFER, NULL);\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tstruct sway_node *node = config->handler_context.node;\n\tstruct sway_container *container = config->handler_context.container;\n\tstruct sway_workspace *workspace = config->handler_context.workspace;\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tif (node->type < N_WORKSPACE) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\"Command 'focus' cannot be used above the workspace level.\");\n\t}\n\n\tif (argc == 0) {\n\t\tif (!container) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"No container to focus was specified.\");\n\t\t}\n\n\t\tif (container_is_scratchpad_hidden_or_child(container)) {\n\t\t\troot_scratchpad_show(container);\n\t\t}\n\t\t// if we are switching to a container under a fullscreen window, we first\n\t\t// need to exit fullscreen so that the newly focused container becomes visible\n\t\tstruct sway_container *obstructing = container_obstructing_fullscreen_container(container);\n\t\tif (obstructing) {\n\t\t\tcontainer_fullscreen_disable(obstructing);\n\t\t\tarrange_root();\n\t\t}\n\t\tseat_set_focus_container(seat, container);\n\t\tseat_consider_warp_to_focus(seat);\n\t\tcontainer_raise_floating(container);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tif (strcmp(argv[0], \"floating\") == 0) {\n\t\treturn focus_mode(workspace, seat, true);\n\t} else if (strcmp(argv[0], \"tiling\") == 0) {\n\t\treturn focus_mode(workspace, seat, false);\n\t} else if (strcmp(argv[0], \"mode_toggle\") == 0) {\n\t\tbool floating = container && container_is_floating_or_child(container);\n\t\treturn focus_mode(workspace, seat, !floating);\n\t}\n\n\tif (strcmp(argv[0], \"output\") == 0) {\n\t\targc--; argv++;\n\t\treturn focus_output(seat, argc, argv);\n\t}\n\n\tif (strcasecmp(argv[0], \"parent\") == 0) {\n\t\treturn focus_parent();\n\t}\n\tif (strcasecmp(argv[0], \"child\") == 0) {\n\t\treturn focus_child();\n\t}\n\n\tenum wlr_direction direction = 0;\n\tbool descend = true;\n\tif (!parse_direction(argv[0], &direction)) {\n\t\tif (!get_direction_from_next_prev(container, seat, argv[0], &direction)) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'focus <direction|next|prev|parent|child|mode_toggle|floating|tiling>' \"\n\t\t\t\t\"or 'focus output <direction|name>'\");\n\t\t} else if (argc == 2 && strcasecmp(argv[1], \"sibling\") == 0) {\n\t\t\tdescend = false;\n\t\t}\n\t}\n\n\tif (!direction) {\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tif (node->type == N_WORKSPACE) {\n\t\t// Jump to the next output\n\t\tstruct sway_output *new_output =\n\t\t\toutput_get_in_direction(workspace->output, direction);\n\t\tif (!new_output) {\n\t\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t\t}\n\n\t\tstruct sway_node *node =\n\t\t\tget_node_in_output_direction(new_output, direction);\n\t\tseat_set_focus(seat, node);\n\t\tseat_consider_warp_to_focus(seat);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tif (!sway_assert(container, \"Expected container to be non null\")) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"\");\n\t}\n\tstruct sway_node *next_focus = NULL;\n\tif (container_is_floating(container) &&\n\t\t\tcontainer->pending.fullscreen_mode == FULLSCREEN_NONE) {\n\t\tnext_focus = node_get_in_direction_floating(container, seat, direction);\n\t} else {\n\t\tnext_focus = node_get_in_direction_tiling(container, seat, direction, descend);\n\t}\n\tif (next_focus) {\n\t\tseat_set_focus(seat, next_focus);\n\t\tseat_consider_warp_to_focus(seat);\n\n\t\tif (next_focus->type == N_CONTAINER) {\n\t\t\tcontainer_raise_floating(next_focus->sway_container);\n\t\t}\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/focus_follows_mouse.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"focus_follows_mouse\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t} else if(strcmp(argv[0], \"no\") == 0) {\n\t\tconfig->focus_follows_mouse = FOLLOWS_NO;\n\t} else if(strcmp(argv[0], \"yes\") == 0) {\n\t\tconfig->focus_follows_mouse = FOLLOWS_YES;\n\t} else if(strcmp(argv[0], \"always\") == 0) {\n\t\tconfig->focus_follows_mouse = FOLLOWS_ALWAYS;\n\t} else {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Expected 'focus_follows_mouse no|yes|always'\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/focus_on_window_activation.c",
    "content": "#include \"sway/commands.h\"\n\nstruct cmd_results *cmd_focus_on_window_activation(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"focus_on_window_activation\",\n\t\t\t\t\tEXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcmp(argv[0], \"smart\") == 0) {\n\t\tconfig->focus_on_window_activation = FOWA_SMART;\n\t} else if (strcmp(argv[0], \"urgent\") == 0) {\n\t\tconfig->focus_on_window_activation = FOWA_URGENT;\n\t} else if (strcmp(argv[0], \"focus\") == 0) {\n\t\tconfig->focus_on_window_activation = FOWA_FOCUS;\n\t} else if (strcmp(argv[0], \"none\") == 0) {\n\t\tconfig->focus_on_window_activation = FOWA_NONE;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected \"\n\t\t\t\t\"'focus_on_window_activation smart|urgent|focus|none'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/focus_wrapping.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_focus_wrapping(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"focus_wrapping\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcasecmp(argv[0], \"force\") == 0) {\n\t\tconfig->focus_wrapping = WRAP_FORCE;\n\t} else if (strcasecmp(argv[0], \"workspace\") == 0) {\n\t\tconfig->focus_wrapping = WRAP_WORKSPACE;\n\t} else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) {\n\t\tconfig->focus_wrapping = WRAP_YES;\n\t} else {\n\t\tconfig->focus_wrapping = WRAP_NO;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/font.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include <pango/pangocairo.h>\n\nstruct cmd_results *cmd_font(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"font\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tchar *font = join_args(argv, argc);\n\tfree(config->font);\n\n\tif (has_prefix(font, \"pango:\")) {\n\t\tconfig->pango_markup = true;\n\t\tconfig->font = strdup(font + strlen(\"pango:\"));\n\t\tfree(font);\n\t} else {\n\t\tconfig->pango_markup = false;\n\t\tconfig->font = font;\n\t}\n\n\t// Parse the font early so we can reject it if it's not valid for pango.\n\t// Also avoids re-parsing each time we render text.\n\tPangoFontDescription *font_description = pango_font_description_from_string(config->font);\n\n\tconst char *family = pango_font_description_get_family(font_description);\n\tif (family == NULL) {\n\t\tpango_font_description_free(font_description);\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid font family.\");\n\t}\n\n\tconst gint size = pango_font_description_get_size(font_description);\n\tif (size == 0) {\n\t\tpango_font_description_free(font_description);\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid font size.\");\n\t}\n\n\tif (config->font_description != NULL) {\n\t\tpango_font_description_free(config->font_description);\n\t}\n\n\tconfig->font_description = font_description;\n\tconfig_update_font_height();\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/for_window.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/criteria.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *cmd_for_window(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"for_window\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tchar *err_str = NULL;\n\tstruct criteria *criteria = criteria_parse(argv[0], &err_str);\n\tif (!criteria) {\n\t\terror = cmd_results_new(CMD_INVALID, \"%s\", err_str);\n\t\tfree(err_str);\n\t\treturn error;\n\t}\n\n\tcriteria->type = CT_COMMAND;\n\tcriteria->cmdlist = join_args(argv + 1, argc - 1);\n\n\t// Check if it already exists\n\tif (criteria_already_exists(criteria)) {\n\t\tsway_log(SWAY_DEBUG, \"for_window already exists: '%s' -> '%s'\",\n\t\t\t\tcriteria->raw, criteria->cmdlist);\n\t\tcriteria_destroy(criteria);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tlist_add(config->criteria, criteria);\n\tsway_log(SWAY_DEBUG, \"for_window: '%s' -> '%s' added\", criteria->raw, criteria->cmdlist);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/force_display_urgency_hint.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include <errno.h>\n\nstruct cmd_results *cmd_force_display_urgency_hint(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"force_display_urgency_hint\",\n\t\t\t\t\tEXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\terrno = 0;\n\tchar *end;\n\tint timeout = (int)strtol(argv[0], &end, 10);\n\tif (errno || end == argv[0] || (*end && strcmp(end, \"ms\") != 0)) {\n\t\treturn cmd_results_new(CMD_INVALID, \"timeout integer invalid\");\n\t}\n\n\tif (argc > 1 && strcmp(argv[1], \"ms\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'force_display_urgency_hint <timeout> [ms]'\");\n\t}\n\n\tconfig->urgent_timeout = timeout > 0 ? timeout : 0;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/force_focus_wrapping.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {\n\tsway_log(SWAY_INFO, \"Warning: force_focus_wrapping is deprecated. \"\n\t\t\"Use focus_wrapping instead.\");\n\tif (config->reading) {\n\t\tconfig_add_swaynag_warning(\"force_focus_wrapping is deprecated. \"\n\t\t\t\"Use focus_wrapping instead.\");\n\t}\n\n\tstruct cmd_results *error =\n\t\tcheckarg(argc, \"force_focus_wrapping\", EXPECTED_EQUAL_TO, 1);\n\tif (error) {\n\t\treturn error;\n\t}\n\n\tif (parse_boolean(argv[0], config->focus_wrapping == WRAP_FORCE)) {\n\t\tconfig->focus_wrapping = WRAP_FORCE;\n\t} else {\n\t\tconfig->focus_wrapping = WRAP_YES;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/fullscreen.c",
    "content": "#include <strings.h>\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"util.h\"\n\n// fullscreen [enable|disable|toggle] [global]\nstruct cmd_results *cmd_fullscreen(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"fullscreen\", EXPECTED_AT_MOST, 2))) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tstruct sway_container *container = config->handler_context.container;\n\n\tif (!container) {\n\t\t// If the focus is not a container, do nothing successfully\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t} else if (!container->pending.workspace) {\n\t\t// If in the scratchpad, operate on the highest container\n\t\twhile (container->pending.parent) {\n\t\t\tcontainer = container->pending.parent;\n\t\t}\n\t}\n\n\tbool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE;\n\tbool global = false;\n\tbool enable = !is_fullscreen;\n\n\tif (argc >= 1) {\n\t\tif (strcasecmp(argv[0], \"global\") == 0) {\n\t\t\tglobal = true;\n\t\t} else {\n\t\t\tenable = parse_boolean(argv[0], is_fullscreen);\n\t\t}\n\t}\n\n\tif (argc >= 2) {\n\t\tglobal = strcasecmp(argv[1], \"global\") == 0;\n\t}\n\n\tenum sway_fullscreen_mode mode = FULLSCREEN_NONE;\n\tif (enable) {\n\t\tmode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE;\n\t}\n\n\tcontainer_set_fullscreen(container, mode);\n\tarrange_root();\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/gaps.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/workspace.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include <math.h>\n\nenum gaps_op {\n\tGAPS_OP_SET,\n\tGAPS_OP_ADD,\n\tGAPS_OP_SUBTRACT,\n\tGAPS_OP_TOGGLE\n};\n\nstruct gaps_data {\n\tbool inner;\n\tstruct {\n\t\tbool top;\n\t\tbool right;\n\t\tbool bottom;\n\t\tbool left;\n\t} outer;\n\tenum gaps_op operation;\n\tint amount;\n};\n\n// Prevent negative outer gaps from moving windows out of the workspace.\nstatic void prevent_invalid_outer_gaps(void) {\n\tif (config->gaps_outer.top < -config->gaps_inner) {\n\t\tconfig->gaps_outer.top = -config->gaps_inner;\n\t}\n\tif (config->gaps_outer.right < -config->gaps_inner) {\n\t\tconfig->gaps_outer.right = -config->gaps_inner;\n\t}\n\tif (config->gaps_outer.bottom < -config->gaps_inner) {\n\t\tconfig->gaps_outer.bottom = -config->gaps_inner;\n\t}\n\tif (config->gaps_outer.left < -config->gaps_inner) {\n\t\tconfig->gaps_outer.left = -config->gaps_inner;\n\t}\n}\n\n// gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>\nstatic const char expected_defaults[] =\n\t\"'gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>'\";\nstatic struct cmd_results *gaps_set_defaults(int argc, char **argv) {\n\tstruct cmd_results *error = checkarg(argc, \"gaps\", EXPECTED_EQUAL_TO, 2);\n\tif (error) {\n\t\treturn error;\n\t}\n\n\tchar *end;\n\tint amount = strtol(argv[1], &end, 10);\n\tif (strlen(end) && strcasecmp(end, \"px\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected %s\", expected_defaults);\n\t}\n\n\tbool valid = false;\n\tif (!strcasecmp(argv[0], \"inner\")) {\n\t\tvalid = true;\n\t\tconfig->gaps_inner = (amount >= 0) ? amount : 0;\n\t} else {\n\t\tif (!strcasecmp(argv[0], \"outer\") || !strcasecmp(argv[0], \"vertical\")\n\t\t\t\t|| !strcasecmp(argv[0], \"top\")) {\n\t\t\tvalid = true;\n\t\t\tconfig->gaps_outer.top = amount;\n\t\t}\n\t\tif (!strcasecmp(argv[0], \"outer\") || !strcasecmp(argv[0], \"horizontal\")\n\t\t\t\t|| !strcasecmp(argv[0], \"right\")) {\n\t\t\tvalid = true;\n\t\t\tconfig->gaps_outer.right = amount;\n\t\t}\n\t\tif (!strcasecmp(argv[0], \"outer\") || !strcasecmp(argv[0], \"vertical\")\n\t\t\t\t|| !strcasecmp(argv[0], \"bottom\")) {\n\t\t\tvalid = true;\n\t\t\tconfig->gaps_outer.bottom = amount;\n\t\t}\n\t\tif (!strcasecmp(argv[0], \"outer\") || !strcasecmp(argv[0], \"horizontal\")\n\t\t\t\t|| !strcasecmp(argv[0], \"left\")) {\n\t\t\tvalid = true;\n\t\t\tconfig->gaps_outer.left = amount;\n\t\t}\n\t}\n\tif (!valid) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected %s\", expected_defaults);\n\t}\n\n\tprevent_invalid_outer_gaps();\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic void apply_gaps_op(int *prop, enum gaps_op op, int amount) {\n\tswitch (op) {\n\tcase GAPS_OP_SET:\n\t\t*prop = amount;\n\t\tbreak;\n\tcase GAPS_OP_ADD:\n\t\t*prop += amount;\n\t\tbreak;\n\tcase GAPS_OP_SUBTRACT:\n\t\t*prop -= amount;\n\t\tbreak;\n\tcase GAPS_OP_TOGGLE:\n\t\t*prop = *prop ? 0 : amount;\n\t\tbreak;\n\t}\n}\n\nstatic void configure_gaps(struct sway_workspace *ws, void *_data) {\n\t// Apply operation to gaps\n\tstruct gaps_data *data = _data;\n\tif (data->inner) {\n\t\tapply_gaps_op(&ws->gaps_inner, data->operation, data->amount);\n\t}\n\tif (data->outer.top) {\n\t\tapply_gaps_op(&(ws->gaps_outer.top), data->operation, data->amount);\n\t}\n\tif (data->outer.right) {\n\t\tapply_gaps_op(&(ws->gaps_outer.right), data->operation, data->amount);\n\t}\n\tif (data->outer.bottom) {\n\t\tapply_gaps_op(&(ws->gaps_outer.bottom), data->operation, data->amount);\n\t}\n\tif (data->outer.left) {\n\t\tapply_gaps_op(&(ws->gaps_outer.left), data->operation, data->amount);\n\t}\n\n\t// Prevent invalid gaps configurations.\n\tif (ws->gaps_inner < 0) {\n\t\tws->gaps_inner = 0;\n\t}\n\tprevent_invalid_outer_gaps();\n\tarrange_workspace(ws);\n}\n\n// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all\n// set|plus|minus|toggle <px>\nstatic const char expected_runtime[] = \"'gaps inner|outer|horizontal|vertical|\"\n\t\"top|right|bottom|left current|all set|plus|minus|toggle <px>'\";\nstatic struct cmd_results *gaps_set_runtime(int argc, char **argv) {\n\tstruct cmd_results *error = checkarg(argc, \"gaps\", EXPECTED_EQUAL_TO, 4);\n\tif (error) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\n\tstruct gaps_data data = {0};\n\n\tif (strcasecmp(argv[0], \"inner\") == 0) {\n\t\tdata.inner = true;\n\t} else {\n\t\tdata.outer.top = !strcasecmp(argv[0], \"outer\") ||\n\t\t\t!strcasecmp(argv[0], \"vertical\") || !strcasecmp(argv[0], \"top\");\n\t\tdata.outer.right = !strcasecmp(argv[0], \"outer\") ||\n\t\t\t!strcasecmp(argv[0], \"horizontal\") || !strcasecmp(argv[0], \"right\");\n\t\tdata.outer.bottom = !strcasecmp(argv[0], \"outer\") ||\n\t\t\t!strcasecmp(argv[0], \"vertical\") || !strcasecmp(argv[0], \"bottom\");\n\t\tdata.outer.left = !strcasecmp(argv[0], \"outer\") ||\n\t\t\t!strcasecmp(argv[0], \"horizontal\") || !strcasecmp(argv[0], \"left\");\n\t}\n\tif (!data.inner && !data.outer.top && !data.outer.right &&\n\t\t\t!data.outer.bottom && !data.outer.left) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected %s\", expected_runtime);\n\t}\n\n\tbool all;\n\tif (strcasecmp(argv[1], \"current\") == 0) {\n\t\tall = false;\n\t} else if (strcasecmp(argv[1], \"all\") == 0) {\n\t\tall = true;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected %s\", expected_runtime);\n\t}\n\n\tif (strcasecmp(argv[2], \"set\") == 0) {\n\t\tdata.operation = GAPS_OP_SET;\n\t} else if (strcasecmp(argv[2], \"plus\") == 0) {\n\t\tdata.operation = GAPS_OP_ADD;\n\t} else if (strcasecmp(argv[2], \"minus\") == 0) {\n\t\tdata.operation = GAPS_OP_SUBTRACT;\n\t} else if (strcasecmp(argv[2], \"toggle\") == 0) {\n\t\tdata.operation = GAPS_OP_TOGGLE;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected %s\", expected_runtime);\n\t}\n\n\tchar *end;\n\tdata.amount = strtol(argv[3], &end, 10);\n\tif (strlen(end) && strcasecmp(end, \"px\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected %s\", expected_runtime);\n\t}\n\n\tif (all) {\n\t\troot_for_each_workspace(configure_gaps, &data);\n\t} else {\n\t\tconfigure_gaps(config->handler_context.workspace, &data);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces\n// gaps inner|outer|<dir>|<side> current|all set|plus|minus|toggle <px> - runtime only\n// <dir> = horizontal|vertical\n// <side> = top|right|bottom|left\nstruct cmd_results *cmd_gaps(int argc, char **argv) {\n\tstruct cmd_results *error = checkarg(argc, \"gaps\", EXPECTED_AT_LEAST, 2);\n\tif (error) {\n\t\treturn error;\n\t}\n\n\tif (argc == 2) {\n\t\treturn gaps_set_defaults(argc, argv);\n\t}\n\tif (argc == 4 && !config->reading) {\n\t\treturn gaps_set_runtime(argc, argv);\n\t}\n\tif (config->reading) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected %s\", expected_defaults);\n\t}\n\treturn cmd_results_new(CMD_INVALID, \"Expected %s or %s\",\n\t\t\texpected_runtime, expected_defaults);\n}\n"
  },
  {
    "path": "sway/commands/gesture.c",
    "content": "#include \"sway/config.h\"\n\n#include \"gesture.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"sway/commands.h\"\n\nvoid free_gesture_binding(struct sway_gesture_binding *binding) {\n\tif (!binding) {\n\t\treturn;\n\t}\n\tfree(binding->input);\n\tfree(binding->command);\n\tfree(binding);\n}\n\n/**\n * Returns true if the bindings have the same gesture type, direction, etc\n */\nstatic bool binding_gesture_equal(struct sway_gesture_binding *binding_a,\n\t\t\t\t\t\t\t\t  struct sway_gesture_binding *binding_b) {\n\tif (strcmp(binding_a->input, binding_b->input) != 0) {\n\t\treturn false;\n\t}\n\n\tif (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {\n\t\treturn false;\n\t}\n\n\tif ((binding_a->flags & BINDING_EXACT) !=\n\t\t(binding_b->flags & BINDING_EXACT)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/**\n * Add gesture binding to config\n */\nstatic struct cmd_results *gesture_binding_add(\n\t\tstruct sway_gesture_binding *binding,\n\t\tconst char *gesturecombo, bool warn) {\n\tlist_t *mode_bindings = config->current_mode->gesture_bindings;\n\t// overwrite the binding if it already exists\n\tbool overwritten = false;\n\tfor (int i = 0; i < mode_bindings->length; ++i) {\n\t\tstruct sway_gesture_binding *config_binding = mode_bindings->items[i];\n\t\tif (binding_gesture_equal(binding, config_binding)) {\n\t\t\tsway_log(SWAY_INFO, \"Overwriting binding '%s' to `%s` from `%s`\",\n\t\t\t\t\tgesturecombo, binding->command, config_binding->command);\n\t\t\tif (warn) {\n\t\t\t\tconfig_add_swaynag_warning(\"Overwriting binding\"\n\t\t\t\t\t\t\"'%s' to `%s` from `%s`\",\n\t\t\t\t\t\tgesturecombo, binding->command,\n\t\t\t\t\t\tconfig_binding->command);\n\t\t\t}\n\t\t\tfree_gesture_binding(config_binding);\n\t\t\tmode_bindings->items[i] = binding;\n\t\t\toverwritten = true;\n\t\t}\n\t}\n\n\tif (!overwritten) {\n\t\tlist_add(mode_bindings, binding);\n\t\tsway_log(SWAY_DEBUG, \"bindgesture - Bound %s to command `%s`\",\n\t\t\t\tgesturecombo, binding->command);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n/**\n * Remove gesture binding from config\n */\nstatic struct cmd_results *gesture_binding_remove(\n\t\tstruct sway_gesture_binding *binding, const char *gesturecombo) {\n\tlist_t *mode_bindings = config->current_mode->gesture_bindings;\n\tfor (int i = 0; i < mode_bindings->length; ++i) {\n\t\tstruct sway_gesture_binding *config_binding = mode_bindings->items[i];\n\t\tif (binding_gesture_equal(binding, config_binding)) {\n\t\t\tfree_gesture_binding(config_binding);\n\t\t\tfree_gesture_binding(binding);\n\t\t\tlist_del(mode_bindings, i);\n\t\t\tsway_log(SWAY_DEBUG, \"unbindgesture - Unbound %s gesture\",\n\t\t\t\t\tgesturecombo);\n\t\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t\t}\n\t}\n\n\tfree_gesture_binding(binding);\n\treturn cmd_results_new(CMD_FAILURE, \"Could not find gesture binding `%s`\",\n\t\t\tgesturecombo);\n}\n\n/**\n * Parse and execute bindgesture or unbindgesture command.\n */\nstatic struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {\n\tint minargs = 2;\n\tchar *bindtype = \"bindgesture\";\n\tif (unbind) {\n\t\tminargs--;\n\t\tbindtype = \"unbindgesture\";\n\t}\n\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {\n\t\treturn error;\n\t}\n\tstruct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));\n\tif (!binding) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate binding\");\n\t}\n\tbinding->input = strdup(\"*\");\n\n\tbool warn = true;\n\n\t// Handle flags\n\twhile (argc > 0) {\n\t\tif (strcmp(\"--exact\", argv[0]) == 0) {\n\t\t\tbinding->flags |= BINDING_EXACT;\n\t\t} else if (strcmp(\"--no-warn\", argv[0]) == 0) {\n\t\t\twarn = false;\n\t\t} else if (has_prefix(argv[0], \"--input-device=\")) {\n\t\t\tfree(binding->input);\n\t\t\tbinding->input = strdup(argv[0] + strlen(\"--input-device=\"));\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t\targv++;\n\t\targc--;\n\t}\n\n\tif (argc < minargs) {\n\t\tfree(binding);\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Invalid %s command (expected at least %d \"\n\t\t\t\t\"non-option arguments, got %d)\", bindtype, minargs, argc);\n\t}\n\n\tchar* errmsg = NULL;\n\tif ((errmsg = gesture_parse(argv[0], &binding->gesture))) {\n\t\tfree(binding);\n\t\tstruct cmd_results *final = cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Invalid %s command (%s)\",\n\t\t\t\tbindtype, errmsg);\n\t\tfree(errmsg);\n\t\treturn final;\n\t}\n\n\tif (unbind) {\n\t\treturn gesture_binding_remove(binding, argv[0]);\n\t}\n\tbinding->command = join_args(argv + 1, argc - 1);\n\treturn gesture_binding_add(binding, argv[0], warn);\n}\n\nstruct cmd_results *cmd_bindgesture(int argc, char **argv) {\n\treturn cmd_bind_or_unbind_gesture(argc, argv, false);\n}\n\nstruct cmd_results *cmd_unbindgesture(int argc, char **argv) {\n\treturn cmd_bind_or_unbind_gesture(argc, argv, true);\n}\n"
  },
  {
    "path": "sway/commands/hide_edge_borders.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n\nstruct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {\n\tconst char *expected_syntax = \"Expected 'hide_edge_borders [--i3] \"\n\t\t\"none|vertical|horizontal|both|smart|smart_no_gaps\";\n\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"hide_edge_borders\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tbool hide_lone_tab = false;\n\tif (strcmp(*argv, \"--i3\") == 0) {\n\t\thide_lone_tab = true;\n\t\t++argv;\n\t\t--argc;\n\t}\n\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\n\tif (strcmp(argv[0], \"none\") == 0) {\n\t\tconfig->hide_edge_borders = E_NONE;\n\t} else if (strcmp(argv[0], \"vertical\") == 0) {\n\t\tconfig->hide_edge_borders = E_VERTICAL;\n\t} else if (strcmp(argv[0], \"horizontal\") == 0) {\n\t\tconfig->hide_edge_borders = E_HORIZONTAL;\n\t} else if (strcmp(argv[0], \"both\") == 0) {\n\t\tconfig->hide_edge_borders = E_BOTH;\n\t} else if (strcmp(argv[0], \"smart\") == 0) {\n\t\tconfig->hide_edge_borders = E_NONE;\n\t\tconfig->hide_edge_borders_smart = ESMART_ON;\n\t} else if (strcmp(argv[0], \"smart_no_gaps\") == 0) {\n\t\tconfig->hide_edge_borders = E_NONE;\n\t\tconfig->hide_edge_borders_smart = ESMART_NO_GAPS;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\tconfig->hide_lone_tab = hide_lone_tab;\n\n\tarrange_root();\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/include.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *cmd_include(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"include\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tchar *files = join_args(argv, argc);\n\t// We don't care if the included config(s) fails to load.\n\tload_include_configs(files, config, &config->swaynag_config_errors);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/inhibit_idle.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/desktop/idle_inhibit_v1.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n\nstruct cmd_results *cmd_inhibit_idle(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"inhibit_idle\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_container *con = config->handler_context.container;\n\tif (!con || !con->view) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Only views can have idle inhibitors\");\n\t}\n\n\tbool clear = false;\n\tenum sway_idle_inhibit_mode mode;\n\tif (strcmp(argv[0], \"focus\") == 0) {\n\t\tmode = INHIBIT_IDLE_FOCUS;\n\t} else if (strcmp(argv[0], \"fullscreen\") == 0) {\n\t\tmode = INHIBIT_IDLE_FULLSCREEN;\n\t} else if (strcmp(argv[0], \"open\") == 0) {\n\t\tmode = INHIBIT_IDLE_OPEN;\n\t} else if (strcmp(argv[0], \"none\") == 0) {\n\t\tclear = true;\n\t} else if (strcmp(argv[0], \"visible\") == 0) {\n\t\tmode = INHIBIT_IDLE_VISIBLE;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected `inhibit_idle focus|fullscreen|open|none|visible`\");\n\t}\n\n\tstruct sway_idle_inhibitor_v1 *inhibitor =\n\t\tsway_idle_inhibit_v1_user_inhibitor_for_view(con->view);\n\tif (inhibitor) {\n\t\tif (clear) {\n\t\t\tsway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor);\n\t\t} else {\n\t\t\tinhibitor->mode = mode;\n\t\t\tsway_idle_inhibit_v1_check_active();\n\t\t}\n\t} else if (!clear) {\n\t\tsway_idle_inhibit_v1_user_inhibitor_register(con->view, mode);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/accel_profile.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n\nstruct cmd_results *input_cmd_accel_profile(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"accel_profile\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"adaptive\") == 0) {\n\t\tic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;\n\t} else if (strcasecmp(argv[0], \"flat\") == 0) {\n\t\tic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'accel_profile <adaptive|flat>'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/calibration_matrix.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_calibration_matrix(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"calibration_matrix\", EXPECTED_EQUAL_TO, 6))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tfloat parsed[6];\n\tfor (int i = 0; i < argc; ++i) {\n\t\tchar *item = argv[i];\n\t\tfloat x = parse_float(item);\n\t\tif (isnan(x)) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"calibration_matrix: unable to parse float: %s\", item);\n\t\t}\n\t\tparsed[i] = x;\n\t}\n\n\tic->calibration_matrix.configured = true;\n\tmemcpy(ic->calibration_matrix.matrix, parsed, sizeof(ic->calibration_matrix.matrix));\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/click_method.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/input-manager.h\"\n#include \"log.h\"\n\nstruct cmd_results *input_cmd_click_method(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"click_method\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"none\") == 0) {\n\t\tic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;\n\t} else if (strcasecmp(argv[0], \"button_areas\") == 0) {\n\t\tic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;\n\t} else if (strcasecmp(argv[0], \"clickfinger\") == 0) {\n\t\tic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'click_method <none|button_areas|clickfinger>'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/clickfinger_button_map.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n\nstruct cmd_results *input_cmd_clickfinger_button_map(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"clickfinger_button_map\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"lrm\") == 0) {\n\t\tic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM;\n\t} else if (strcasecmp(argv[0], \"lmr\") == 0) {\n\t\tic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'clickfinger_button_map <lrm|lmr>'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/drag.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_drag(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"drag\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (parse_boolean(argv[0], true)) {\n\t\tic->drag = LIBINPUT_CONFIG_DRAG_ENABLED;\n\t} else {\n\t\tic->drag = LIBINPUT_CONFIG_DRAG_DISABLED;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/drag_lock.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_drag_lock(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"drag_lock\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY\n\tif (strcmp(argv[0], \"enabled_sticky\") == 0) {\n\t\tic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY;\n\t} else\n#endif\n\tif (parse_boolean(argv[0], true)) {\n\t\tic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;\n\t} else {\n\t\tic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/dwt.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_dwt(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"dwt\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (parse_boolean(argv[0], true)) {\n\t\tic->dwt = LIBINPUT_CONFIG_DWT_ENABLED;\n\t} else {\n\t\tic->dwt = LIBINPUT_CONFIG_DWT_DISABLED;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/dwtp.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_dwtp(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"dwtp\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (parse_boolean(argv[0], true)) {\n\t\tic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED;\n\t} else {\n\t\tic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/events.c",
    "content": "#include <limits.h>\n#include <string.h>\n#include <strings.h>\n#include <wlr/config.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/server.h\"\n#include \"log.h\"\n\n#if WLR_HAS_LIBINPUT_BACKEND\n#include <wlr/backend/libinput.h>\n#endif\n\nstatic void toggle_supported_send_events_for_device(struct input_config *ic,\n\t\tstruct sway_input_device *input_device) {\n#if WLR_HAS_LIBINPUT_BACKEND\n\tstruct wlr_input_device *wlr_device = input_device->wlr_device;\n\tif (!wlr_input_device_is_libinput(wlr_device)) {\n\t\treturn;\n\t}\n\tstruct libinput_device *libinput_dev =\n\t\twlr_libinput_get_device_handle(wlr_device);\n\n\tenum libinput_config_send_events_mode mode =\n\t\tlibinput_device_config_send_events_get_mode(libinput_dev);\n\tuint32_t possible =\n\t\tlibinput_device_config_send_events_get_modes(libinput_dev);\n\n\tswitch (mode) {\n\tcase LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:\n\t\tmode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;\n\t\tif (possible & mode) {\n\t\t\tbreak;\n\t\t}\n\t\t// fall through\n\tcase LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:\n\t\tmode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;\n\t\tif (possible & mode) {\n\t\t\tbreak;\n\t\t}\n\t\t// fall through\n\tcase LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:\n\tdefault:\n\t\tmode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;\n\t\tbreak;\n\t}\n\n\tic->send_events = mode;\n#endif\n}\n\nstatic int mode_for_name(const char *name) {\n\tif (!strcmp(name, \"enabled\")) {\n\t\treturn LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;\n\t} else if (!strcmp(name, \"disabled_on_external_mouse\")) {\n\t\treturn LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;\n\t} else if (!strcmp(name, \"disabled\")) {\n\t\treturn LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;\n\t}\n\treturn -1;\n}\n\nstatic void toggle_select_send_events_for_device(struct input_config *ic,\n\t\tstruct sway_input_device *input_device, int argc, char **argv) {\n#if WLR_HAS_LIBINPUT_BACKEND\n\tif (!wlr_input_device_is_libinput(input_device->wlr_device)) {\n\t\treturn;\n\t}\n\t// Get the currently set event mode since ic is a new config that will be\n\t// merged on the existing later. It should be set to INT_MIN before this.\n\tic->send_events = libinput_device_config_send_events_get_mode(\n\t\t\twlr_libinput_get_device_handle(input_device->wlr_device));\n\n\tint index;\n\tfor (index = 0; index < argc; ++index) {\n\t\tif (mode_for_name(argv[index]) == ic->send_events) {\n\t\t\t++index;\n\t\t\tbreak;\n\t\t}\n\t}\n\tic->send_events = mode_for_name(argv[index % argc]);\n#endif\n}\n\nstatic void toggle_send_events(int argc, char **argv) {\n\tstruct input_config *ic = config->handler_context.input_config;\n\tbool wildcard = strcmp(ic->identifier, \"*\") == 0;\n\tconst char *type = has_prefix(ic->identifier, \"type:\")\n\t\t? ic->identifier + strlen(\"type:\") : NULL;\n\tstruct sway_input_device *device = NULL;\n\twl_list_for_each(device, &server.input->devices, link) {\n\t\tif (wildcard || type) {\n\t\t\tic = new_input_config(device->identifier);\n\t\t\tif (!ic) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (type && strcmp(input_device_get_type(device), type) != 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else if (strcmp(ic->identifier, device->identifier) != 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (argc) {\n\t\t\ttoggle_select_send_events_for_device(ic, device, argc, argv);\n\t\t} else {\n\t\t\ttoggle_supported_send_events_for_device(ic, device);\n\t\t}\n\n\t\tif (wildcard || type) {\n\t\t\tstore_input_config(ic, NULL);\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstruct cmd_results *input_cmd_events(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"events\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"enabled\") == 0) {\n\t\tic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;\n\t} else if (strcasecmp(argv[0], \"disabled\") == 0) {\n\t\tic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;\n\t} else if (strcasecmp(argv[0], \"disabled_on_external_mouse\") == 0) {\n\t\tic->send_events =\n\t\t\tLIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;\n\t} else if (config->reading) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'events <enabled|disabled|disabled_on_external_mouse>'\");\n\t} else if (strcasecmp(argv[0], \"toggle\") == 0) {\n\t\tfor (int i = 1; i < argc; ++i) {\n\t\t\tif (mode_for_name(argv[i]) == -1) {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Invalid toggle mode %s\", argv[i]);\n\t\t\t}\n\t\t}\n\n\t\ttoggle_send_events(argc - 1, argv + 1);\n\n\t\tif (strcmp(ic->identifier, \"*\") == 0 || has_prefix(ic->identifier, \"type:\")) {\n\t\t\t// Update the device input configs and then reset the type/wildcard\n\t\t\t// config send events mode so that is does not override the device\n\t\t\t// ones. The device ones will be applied when attempting to apply\n\t\t\t// the type/wildcard config\n\t\t\tic->send_events = INT_MIN;\n\t\t}\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'events <enabled|disabled|disabled_on_external_mouse|\"\n\t\t\t\"toggle>'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/left_handed.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_left_handed(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"left_handed\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->left_handed = parse_boolean(argv[0], true);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/map_from_region.c",
    "content": "#include <stdbool.h>\n#include <string.h>\n#include <strings.h>\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/input-manager.h\"\n\nstatic bool parse_coords(const char *str, double *x, double *y, bool *mm) {\n\t*mm = false;\n\n\tchar *end;\n\n\t// Check for \"0x\" prefix to avoid strtod treating the string as hex\n\tif (str[0] == '0' && str[1] == 'x') {\n\t\tif (strlen(str) < 3) {\n\t\t\treturn false;\n\t\t}\n\t\t*x = 0;\n\t\tend = (char *)str + 2;\n\t} else {\n\t\t*x = strtod(str, &end);\n\t\tif (end[0] != 'x') {\n\t\t\treturn false;\n\t\t}\n\t\t++end;\n\t}\n\n\t*y = strtod(end, &end);\n\tif (end[0] == 'm') {\n\t\t// Expect mm\n\t\tif (end[1] != 'm') {\n\t\t\treturn false;\n\t\t}\n\t\t*mm = true;\n\t\tend = &end[2];\n\t}\n\tif (end[0] != '\\0') {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstruct cmd_results *input_cmd_map_from_region(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"map_from_region\", EXPECTED_EQUAL_TO, 2))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined\");\n\t}\n\n\tic->mapped_from_region =\n\t\tcalloc(1, sizeof(struct input_config_mapped_from_region));\n\n\tbool mm1, mm2;\n\tif (!parse_coords(argv[0], &ic->mapped_from_region->x1,\n\t\t\t&ic->mapped_from_region->y1, &mm1)) {\n\t\tfree(ic->mapped_from_region);\n\t\tic->mapped_from_region = NULL;\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid top-left coordinates\");\n\t}\n\tif (!parse_coords(argv[1], &ic->mapped_from_region->x2,\n\t\t\t&ic->mapped_from_region->y2, &mm2)) {\n\t\tfree(ic->mapped_from_region);\n\t\tic->mapped_from_region = NULL;\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid bottom-right coordinates\");\n\t}\n\tif (ic->mapped_from_region->x1 > ic->mapped_from_region->x2 ||\n\t\t\tic->mapped_from_region->y1 > ic->mapped_from_region->y2) {\n\t\tfree(ic->mapped_from_region);\n\t\tic->mapped_from_region = NULL;\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid rectangle\");\n\t}\n\tif (mm1 != mm2) {\n\t\tfree(ic->mapped_from_region);\n\t\tic->mapped_from_region = NULL;\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\"Both coordinates must be in the same unit\");\n\t}\n\tic->mapped_from_region->mm = mm1;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/map_to_output.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"log.h\"\n\nstruct cmd_results *input_cmd_map_to_output(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"map_to_output\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->mapped_to = MAPPED_TO_OUTPUT;\n\tic->mapped_to_output = strdup(argv[0]);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/map_to_region.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *input_cmd_map_to_region(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"map_to_region\", EXPECTED_EQUAL_TO, 4))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined\");\n\t}\n\n\tic->mapped_to = MAPPED_TO_REGION;\n\tic->mapped_to_region = calloc(1, sizeof(struct wlr_box));\n\n\tconst char *errstr;\n\tchar *end;\n\n\tic->mapped_to_region->x = strtol(argv[0], &end, 10);\n\tif (end[0] != '\\0') {\n\t\terrstr = \"Invalid X coordinate\";\n\t\tgoto error;\n\t}\n\n\tic->mapped_to_region->y = strtol(argv[1], &end, 10);\n\tif (end[0] != '\\0') {\n\t\terrstr = \"Invalid Y coordinate\";\n\t\tgoto error;\n\t}\n\n\tic->mapped_to_region->width = strtol(argv[2], &end, 10);\n\tif (end[0] != '\\0' || ic->mapped_to_region->width < 1) {\n\t\terrstr = \"Invalid width\";\n\t\tgoto error;\n\t}\n\n\tic->mapped_to_region->height = strtol(argv[3], &end, 10);\n\tif (end[0] != '\\0' || ic->mapped_to_region->height < 1) {\n\t\terrstr = \"Invalid height\";\n\t\tgoto error;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\nerror:\n\tfree(ic->mapped_to_region);\n\tic->mapped_to_region = NULL;\n\treturn cmd_results_new(CMD_FAILURE, \"%s\", errstr);\n}\n"
  },
  {
    "path": "sway/commands/input/middle_emulation.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"middle_emulation\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (parse_boolean(argv[0], true)) {\n\t\tic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;\n\t} else {\n\t\tic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/natural_scroll.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"natural_scroll\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->natural_scroll = parse_boolean(argv[0], true);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/pointer_accel.c",
    "content": "#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"pointer_accel\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tfloat pointer_accel = parse_float(argv[0]);\n\tif (isnan(pointer_accel)) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Invalid pointer accel; expected float.\");\n\t} if (pointer_accel < -1 || pointer_accel > 1) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Input out of range [-1, 1]\");\n\t}\n\tic->pointer_accel = pointer_accel;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/repeat_delay.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n\nstruct cmd_results *input_cmd_repeat_delay(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"repeat_delay\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tint repeat_delay = atoi(argv[0]);\n\tif (repeat_delay < 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Repeat delay cannot be negative\");\n\t}\n\tic->repeat_delay = repeat_delay;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/repeat_rate.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n\nstruct cmd_results *input_cmd_repeat_rate(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"repeat_rate\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tint repeat_rate = atoi(argv[0]);\n\tif (repeat_rate < 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Repeat rate cannot be negative\");\n\t}\n\tic->repeat_rate = repeat_rate;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/rotation_angle.c",
    "content": "#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_rotation_angle(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"rotation_angle\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tfloat rotation_angle = parse_float(argv[0]);\n\tif (isnan(rotation_angle)) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Invalid rotation_angle; expected float.\");\n\t} if (rotation_angle < 0 || rotation_angle > 360) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Input out of range [0, 360)\");\n\t}\n\tic->rotation_angle = rotation_angle;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/scroll_button.c",
    "content": "#include <libevdev/libevdev.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/cursor.h\"\n\nstruct cmd_results *input_cmd_scroll_button(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"scroll_button\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcmp(*argv, \"disable\") == 0) {\n\t\tic->scroll_button = 0;\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tchar *message = NULL;\n\tuint32_t button = get_mouse_button(*argv, &message);\n\tif (message) {\n\t\terror = cmd_results_new(CMD_INVALID, \"%s\", message);\n\t\tfree(message);\n\t\treturn error;\n\t} else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN\n\t\t\t|| button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"X11 axis buttons are not supported for scroll_button\");\n\t} else if (!button) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Unknown button %s\", *argv);\n\t}\n\tic->scroll_button = button;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/scroll_button_lock.c",
    "content": "#include <libinput.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"scroll_button_lock\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (parse_boolean(argv[0], true)) {\n\t\tic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED;\n\t} else {\n\t\tic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/scroll_factor.c",
    "content": "#include <errno.h>\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_scroll_factor(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"scroll_factor\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tfloat scroll_factor = parse_float(argv[0]);\n\tif (isnan(scroll_factor)) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Invalid scroll factor; expected float.\");\n\t} else if (scroll_factor < 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Scroll factor cannot be negative.\");\n\t}\n\tic->scroll_factor = scroll_factor;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/scroll_method.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n\nstruct cmd_results *input_cmd_scroll_method(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"scroll_method\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"none\") == 0) {\n\t\tic->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;\n\t} else if (strcasecmp(argv[0], \"two_finger\") == 0) {\n\t\tic->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;\n\t} else if (strcasecmp(argv[0], \"edge\") == 0) {\n\t\tic->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE;\n\t} else if (strcasecmp(argv[0], \"on_button_down\") == 0) {\n\t\tic->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'scroll_method <none|two_finger|edge|on_button_down>'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/tap.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_tap(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"tap\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (parse_boolean(argv[0], true)) {\n\t\tic->tap = LIBINPUT_CONFIG_TAP_ENABLED;\n\t} else {\n\t\tic->tap = LIBINPUT_CONFIG_TAP_DISABLED;\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/tap_button_map.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n\nstruct cmd_results *input_cmd_tap_button_map(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"tap_button_map\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"lrm\") == 0) {\n\t\tic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;\n\t} else if (strcasecmp(argv[0], \"lmr\") == 0) {\n\t\tic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'tap_button_map <lrm|lmr>'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/tool_mode.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstatic void set_tool_mode(struct input_config *ic,\n\t\tenum wlr_tablet_tool_type type, enum sway_tablet_tool_mode mode) {\n\tfor (int i = 0; i < ic->tools->length; i++) {\n\t\tstruct input_config_tool *tool = ic->tools->items[i];\n\t\tif (tool->type == type) {\n\t\t\ttool->mode = mode;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tstruct input_config_tool *tool = calloc(1, sizeof(*tool));\n\tif (tool) {\n\t\ttool->type = type;\n\t\ttool->mode = mode;\n\t\tlist_add(ic->tools, tool);\n\t}\n}\n\nstruct cmd_results *input_cmd_tool_mode(int argc, char **argv) {\n\tstruct cmd_results *error;\n\tif ((error = checkarg(argc, \"tool_mode\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tenum sway_tablet_tool_mode tool_mode;\n\tif (!strcasecmp(argv[1], \"absolute\")) {\n\t\ttool_mode = SWAY_TABLET_TOOL_MODE_ABSOLUTE;\n\t} else if (!strcasecmp(argv[1], \"relative\")) {\n\t\ttool_mode = SWAY_TABLET_TOOL_MODE_RELATIVE;\n\t} else {\n\t\tgoto invalid_command;\n\t}\n\n\tif (!strcasecmp(argv[0], \"*\")) {\n\t\tset_tool_mode(ic, WLR_TABLET_TOOL_TYPE_PEN, tool_mode);\n\t\tset_tool_mode(ic, WLR_TABLET_TOOL_TYPE_ERASER, tool_mode);\n\t\tset_tool_mode(ic, WLR_TABLET_TOOL_TYPE_BRUSH, tool_mode);\n\t\tset_tool_mode(ic, WLR_TABLET_TOOL_TYPE_PENCIL, tool_mode);\n\t\tset_tool_mode(ic, WLR_TABLET_TOOL_TYPE_AIRBRUSH, tool_mode);\n\t} else {\n\t\tenum wlr_tablet_tool_type tool_type;\n\t\tif (!strcasecmp(argv[0], \"pen\")) {\n\t\t\ttool_type = WLR_TABLET_TOOL_TYPE_PEN;\n\t\t} else if (!strcasecmp(argv[0], \"eraser\")) {\n\t\t\ttool_type = WLR_TABLET_TOOL_TYPE_ERASER;\n\t\t} else if (!strcasecmp(argv[0], \"brush\")) {\n\t\t\ttool_type = WLR_TABLET_TOOL_TYPE_BRUSH;\n\t\t} else if (!strcasecmp(argv[0], \"pencil\")) {\n\t\t\ttool_type = WLR_TABLET_TOOL_TYPE_PENCIL;\n\t\t} else if (!strcasecmp(argv[0], \"airbrush\")) {\n\t\t\ttool_type = WLR_TABLET_TOOL_TYPE_AIRBRUSH;\n\t\t} else {\n\t\t\tgoto invalid_command;\n\t\t}\n\n\t\tset_tool_mode(ic, tool_type, tool_mode);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\ninvalid_command:\n\treturn cmd_results_new(CMD_INVALID,\n\t\t\"Expected 'tool_mode <tool> <absolute|relative>'\");\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_capslock.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_capslock\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->xkb_capslock = parse_boolean(argv[0], false);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_file.c",
    "content": "#include <unistd.h>\n#include <errno.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *input_cmd_xkb_file(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_file\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (strcmp(argv[0], \"-\") == 0) {\n\t\tfree(ic->xkb_file);\n\t\tic->xkb_file = NULL;\n\t} else {\n\t\tic->xkb_file = strdup(argv[0]);\n\t\tif (!expand_path(&ic->xkb_file)) {\n\t\t\terror = cmd_results_new(CMD_INVALID, \"Invalid syntax (%s)\",\n\t\t\t\t\tic->xkb_file);\n\t\t\tfree(ic->xkb_file);\n\t\t\tic->xkb_file = NULL;\n\t\t\treturn error;\n\t\t}\n\t\tif (!ic->xkb_file) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to allocate expanded path\");\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate resource\");\n\t\t}\n\n\t\tbool can_access = access(ic->xkb_file, F_OK) != -1;\n\t\tif (!can_access) {\n\t\t\tsway_log_errno(SWAY_ERROR, \"Unable to access xkb file '%s'\",\n\t\t\t\t\tic->xkb_file);\n\t\t\tconfig_add_swaynag_warning(\"Unable to access xkb file '%s'\",\n\t\t\t\t\tic->xkb_file);\n\t\t}\n\t}\n\tic->xkb_file_is_set = true;\n\n\tsway_log(SWAY_DEBUG, \"set-xkb_file for config: %s file: %s\",\n\t\t\tic->identifier, ic->xkb_file);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_layout.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_layout\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->xkb_layout = strdup(argv[0]);\n\n\tsway_log(SWAY_DEBUG, \"set-xkb_layout for config: %s layout: %s\",\n\t\t\tic->identifier, ic->xkb_layout);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_model.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *input_cmd_xkb_model(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_model\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->xkb_model = strdup(argv[0]);\n\n\tsway_log(SWAY_DEBUG, \"set-xkb_model for config: %s model: %s\",\n\t\t\tic->identifier, ic->xkb_model);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_numlock.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"util.h\"\n\nstruct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_numlock\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->xkb_numlock = parse_boolean(argv[0], false);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_options.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *input_cmd_xkb_options(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_options\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->xkb_options = strdup(argv[0]);\n\n\tsway_log(SWAY_DEBUG, \"set-xkb_options for config: %s options: %s\",\n\t\t\tic->identifier, ic->xkb_options);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_rules.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_rules\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->xkb_rules = strdup(argv[0]);\n\n\tsway_log(SWAY_DEBUG, \"set-xkb_rules for config: %s rules: %s\",\n\t\t\tic->identifier, ic->xkb_rules);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_switch_layout.c",
    "content": "#include <assert.h>\n#include <wlr/interfaces/wlr_keyboard.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/server.h\"\n#include \"log.h\"\n\nstruct xkb_switch_layout_action {\n\tstruct wlr_keyboard *keyboard;\n\txkb_layout_index_t layout;\n};\n\nstatic void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) {\n\txkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);\n\tif (idx >= num_layouts) {\n\t\treturn;\n\t}\n\twlr_keyboard_notify_modifiers(kbd, kbd->modifiers.depressed,\n\t\tkbd->modifiers.latched, kbd->modifiers.locked, idx);\n}\n\nstatic xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) {\n\txkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);\n\tassert(num_layouts > 0);\n\n\txkb_layout_index_t layout_idx;\n\tfor (layout_idx = 0; layout_idx < num_layouts; layout_idx++) {\n\t\tif (xkb_state_layout_index_is_active(kbd->xkb_state,\n\t\t\t\tlayout_idx, XKB_STATE_LAYOUT_EFFECTIVE)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn layout_idx;\n}\n\nstatic xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) {\n\txkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);\n\txkb_layout_index_t idx = get_current_layout_index(kbd);\n\treturn (idx + num_layouts + dir) % num_layouts;\n}\n\nstruct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_switch_layout\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tif (config->reading || !config->active) {\n\t\treturn cmd_results_new(CMD_DEFER, NULL);\n\t}\n\n\tconst char *layout_str = argv[0];\n\tint relative, layout;\n\n\tif (strcmp(layout_str, \"next\") == 0) {\n\t\trelative = 1;\n\t} else if (strcmp(layout_str, \"prev\") == 0) {\n\t\trelative = -1;\n\t} else {\n\t\tchar *end;\n\t\tlayout = strtol(layout_str, &end, 10);\n\t\tif (layout_str[0] == '\\0' || end[0] != '\\0') {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid argument.\");\n\t\t} else if (layout < 0) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid layout index.\");\n\t\t}\n\t\trelative = 0;\n\t}\n\n\tstruct xkb_switch_layout_action *actions = calloc(\n\t\twl_list_length(&server.input->devices),\n\t\tsizeof(struct xkb_switch_layout_action));\n\tsize_t actions_len = 0;\n\n\tif (!actions) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate actions\");\n\t}\n\n\t/* Calculate new indexes first because switching a layout in one\n\t   keyboard may result in a change on other keyboards as well because\n\t   of keyboard groups. */\n\tstruct sway_input_device *dev;\n\twl_list_for_each(dev, &server.input->devices, link) {\n\t\tif (strcmp(ic->identifier, \"*\") != 0 &&\n\t\t\t\tstrcmp(ic->identifier, \"type:keyboard\") != 0 &&\n\t\t\t\tstrcmp(ic->identifier, dev->identifier) != 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tstruct wlr_keyboard *keyboard =\n\t\t\twlr_keyboard_from_input_device(dev->wlr_device);\n\t\tif (keyboard->keymap == NULL && dev->is_virtual) {\n\t\t\t// The `sway_keyboard_set_layout` function is by default skipped\n\t\t\t// when configuring virtual keyboards.\n\t\t\tcontinue;\n\t\t}\n\n\t\tstruct xkb_switch_layout_action *action =\n\t\t\t&actions[actions_len++];\n\t\taction->keyboard = keyboard;\n\n\t\tif (relative) {\n\t\t\taction->layout = get_layout_relative(action->keyboard, relative);\n\t\t} else {\n\t\t\taction->layout = layout;\n\t\t}\n\t}\n\n\tfor (size_t i = 0; i < actions_len; i++) {\n\t\tswitch_layout(actions[i].keyboard, actions[i].layout);\n\t}\n\tfree(actions);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input/xkb_variant.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"log.h\"\n\nstruct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xkb_variant\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct input_config *ic = config->handler_context.input_config;\n\tif (!ic) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No input device defined.\");\n\t}\n\n\tic->xkb_variant = strdup(argv[0]);\n\n\tsway_log(SWAY_DEBUG, \"set-xkb_variant for config: %s variant: %s\",\n\t\t\tic->identifier, ic->xkb_variant);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/input.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/keyboard.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\n// must be in order for the bsearch\nstatic const struct cmd_handler input_handlers[] = {\n\t{ \"accel_profile\", input_cmd_accel_profile },\n\t{ \"calibration_matrix\", input_cmd_calibration_matrix },\n\t{ \"click_method\", input_cmd_click_method },\n\t{ \"clickfinger_button_map\", input_cmd_clickfinger_button_map },\n\t{ \"drag\", input_cmd_drag },\n\t{ \"drag_lock\", input_cmd_drag_lock },\n\t{ \"dwt\", input_cmd_dwt },\n\t{ \"dwtp\", input_cmd_dwtp },\n\t{ \"events\", input_cmd_events },\n\t{ \"left_handed\", input_cmd_left_handed },\n\t{ \"map_from_region\", input_cmd_map_from_region },\n\t{ \"map_to_output\", input_cmd_map_to_output },\n\t{ \"map_to_region\", input_cmd_map_to_region },\n\t{ \"middle_emulation\", input_cmd_middle_emulation },\n\t{ \"natural_scroll\", input_cmd_natural_scroll },\n\t{ \"pointer_accel\", input_cmd_pointer_accel },\n\t{ \"repeat_delay\", input_cmd_repeat_delay },\n\t{ \"repeat_rate\", input_cmd_repeat_rate },\n\t{ \"rotation_angle\", input_cmd_rotation_angle },\n\t{ \"scroll_button\", input_cmd_scroll_button },\n\t{ \"scroll_button_lock\", input_cmd_scroll_button_lock },\n\t{ \"scroll_factor\", input_cmd_scroll_factor },\n\t{ \"scroll_method\", input_cmd_scroll_method },\n\t{ \"tap\", input_cmd_tap },\n\t{ \"tap_button_map\", input_cmd_tap_button_map },\n\t{ \"tool_mode\", input_cmd_tool_mode },\n\t{ \"xkb_file\", input_cmd_xkb_file },\n\t{ \"xkb_layout\", input_cmd_xkb_layout },\n\t{ \"xkb_model\", input_cmd_xkb_model },\n\t{ \"xkb_options\", input_cmd_xkb_options },\n\t{ \"xkb_rules\", input_cmd_xkb_rules },\n\t{ \"xkb_switch_layout\", input_cmd_xkb_switch_layout },\n\t{ \"xkb_variant\", input_cmd_xkb_variant },\n};\n\n// must be in order for the bsearch\nstatic const struct cmd_handler input_config_handlers[] = {\n\t{ \"xkb_capslock\", input_cmd_xkb_capslock },\n\t{ \"xkb_numlock\", input_cmd_xkb_numlock },\n};\n\nstruct cmd_results *cmd_input(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"input\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"entering input block: %s\", argv[0]);\n\n\tconfig->handler_context.input_config = new_input_config(argv[0]);\n\tif (!config->handler_context.input_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Couldn't allocate config\");\n\t}\n\n\tstruct cmd_results *res;\n\n\tif (find_handler(argv[1], input_config_handlers,\n\t\t\tsizeof(input_config_handlers))) {\n\t\tif (config->reading) {\n\t\t\tres = config_subcommand(argv + 1, argc - 1,\n\t\t\t\tinput_config_handlers, sizeof(input_config_handlers));\n\t\t} else {\n\t\t\tres = cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Can only be used in config file.\");\n\t\t}\n\t} else {\n\t\tres = config_subcommand(argv + 1, argc - 1,\n\t\t\tinput_handlers, sizeof(input_handlers));\n\t}\n\n\tif ((!res || res->status == CMD_SUCCESS) &&\n\t\t\tstrcmp(argv[1], \"xkb_switch_layout\") != 0) {\n\t\tchar *error = NULL;\n\t\tstruct input_config *ic =\n\t\t\tstore_input_config(config->handler_context.input_config, &error);\n\t\tif (!ic) {\n\t\t\tfree_input_config(config->handler_context.input_config);\n\t\t\tif (res) {\n\t\t\t\tfree_cmd_results(res);\n\t\t\t}\n\t\t\tres = cmd_results_new(CMD_FAILURE, \"Failed to compile keymap: %s\",\n\t\t\t\t\terror ? error : \"(details unavailable)\");\n\t\t\tfree(error);\n\t\t\treturn res;\n\t\t}\n\n\t\tif (!config->reading) {\n\t\t\tinput_manager_apply_input_config(ic);\n\t\t}\n\t} else {\n\t\tfree_input_config(config->handler_context.input_config);\n\t}\n\n\tconfig->handler_context.input_config = NULL;\n\n\treturn res;\n}\n"
  },
  {
    "path": "sway/commands/kill.c",
    "content": "#include \"log.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/commands.h\"\n\nstatic void close_container_iterator(struct sway_container *con, void *data) {\n\tif (con->view) {\n\t\tview_close(con->view);\n\t}\n}\n\nstruct cmd_results *cmd_kill(int argc, char **argv) {\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tstruct sway_container *con = config->handler_context.container;\n\tstruct sway_workspace *ws = config->handler_context.workspace;\n\n\tif (con) {\n\t\tclose_container_iterator(con, NULL);\n\t\tcontainer_for_each_child(con, close_container_iterator, NULL);\n\t} else {\n\t\tworkspace_for_each_container(ws, close_container_iterator, NULL);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/layout.c",
    "content": "#include <stdbool.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/workspace.h\"\n#include \"log.h\"\n\nstatic enum sway_container_layout parse_layout_string(char *s) {\n\tif (strcasecmp(s, \"splith\") == 0) {\n\t\treturn L_HORIZ;\n\t} else if (strcasecmp(s, \"splitv\") == 0) {\n\t\treturn L_VERT;\n\t} else if (strcasecmp(s, \"tabbed\") == 0) {\n\t\treturn L_TABBED;\n\t} else if (strcasecmp(s, \"stacking\") == 0) {\n\t\treturn L_STACKED;\n\t}\n\treturn L_NONE;\n}\n\nstatic const char expected_syntax[] =\n\t\"Expected 'layout default|tabbed|stacking|splitv|splith' or \"\n\t\"'layout toggle [split|all]' or \"\n\t\"'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'\";\n\nstatic enum sway_container_layout toggle_split_layout(\n\t\tenum sway_container_layout layout,\n\t\tenum sway_container_layout prev_split_layout,\n\t\tstruct sway_output *output) {\n\tif (layout == L_HORIZ) {\n\t\treturn L_VERT;\n\t} else if (layout == L_VERT) {\n\t\treturn L_HORIZ;\n\t} else if (prev_split_layout != L_NONE) {\n\t\treturn prev_split_layout;\n\t} else if (config->default_orientation != L_NONE) {\n\t\treturn config->default_orientation;\n\t} else if (output->height > output->width) {\n\t\treturn L_VERT;\n\t}\n\treturn L_HORIZ;\n}\n\nstatic enum sway_container_layout get_layout_toggle(int argc, char **argv,\n\t\tenum sway_container_layout layout,\n\t\tenum sway_container_layout prev_split_layout,\n\t\tstruct sway_output *output) {\n\t// \"layout toggle\"\n\tif (argc == 1) {\n\t\treturn toggle_split_layout(layout, prev_split_layout, output);\n\t}\n\n\tif (argc == 2) {\n\t\t// \"layout toggle split\" (same as \"layout toggle\")\n\t\tif (strcasecmp(argv[1], \"split\") == 0) {\n\t\t\treturn toggle_split_layout(layout, prev_split_layout, output);\n\t\t}\n\t\t// \"layout toggle all\"\n\t\tif (strcasecmp(argv[1], \"all\") == 0) {\n\t\t\treturn layout == L_HORIZ ? L_VERT :\n\t\t\t\tlayout == L_VERT ? L_STACKED :\n\t\t\t\tlayout == L_STACKED ? L_TABBED : L_HORIZ;\n\t\t}\n\t\treturn L_NONE;\n\t}\n\n\tenum sway_container_layout parsed;\n\tint curr = 1;\n\tfor (; curr < argc; curr++) {\n\t\tparsed = parse_layout_string(argv[curr]);\n\t\tif (parsed == layout || (strcmp(argv[curr], \"split\") == 0 &&\n\t\t\t\t (layout == L_VERT || layout == L_HORIZ))) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tfor (int i = curr + 1; i != curr; ++i) {\n\t\t// cycle round to find next valid layout\n\t\tif (i >= argc) {\n\t\t\ti = 1;\n\t\t}\n\t\tparsed = parse_layout_string(argv[i]);\n\t\tif (parsed != L_NONE) {\n\t\t\treturn parsed;\n\t\t}\n\t\tif (strcmp(argv[i], \"split\") == 0) {\n\t\t\treturn toggle_split_layout(layout, prev_split_layout, output);\n\t\t}\n\t\t// invalid layout strings are silently ignored\n\t}\n\treturn L_NONE;\n}\n\nstatic enum sway_container_layout get_layout(int argc, char **argv,\n\t\tenum sway_container_layout layout,\n\t\tenum sway_container_layout prev_split_layout,\n\t\tstruct sway_output *output) {\n\t// Check if assigned directly\n\tenum sway_container_layout parsed = parse_layout_string(argv[0]);\n\tif (parsed != L_NONE) {\n\t\treturn parsed;\n\t}\n\n\tif (strcasecmp(argv[0], \"default\") == 0) {\n\t\treturn prev_split_layout;\n\t}\n\n\tif (strcasecmp(argv[0], \"toggle\") == 0) {\n\t\treturn get_layout_toggle(argc, argv, layout, prev_split_layout, output);\n\t}\n\n\treturn L_NONE;\n}\n\nstruct cmd_results *cmd_layout(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"layout\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tstruct sway_container *container = config->handler_context.container;\n\tstruct sway_workspace *workspace = config->handler_context.workspace;\n\n\tif (container && container_is_floating(container)) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Unable to change layout of floating windows\");\n\t}\n\n\t// Operate on parent container, like i3.\n\tif (container) {\n\t\tcontainer = container->pending.parent;\n\t\t// If parent has only a singe child operate on its parent and\n\t\t// flatten once, like i3\n\t\tif (container && container->pending.children->length == 1) {\n\t\t\t// Also check grandparent to avoid restricting layouts\n\t\t\tstruct sway_container *parent = container->pending.parent;\n\t\t\tif (parent && parent->pending.children->length == 1) {\n\t\t\t\tstruct sway_container *child = container->pending.children->items[0];\n\t\t\t\tstruct sway_container *parent = container->pending.parent;\n\t\t\t\tcontainer_replace(container, child);\n\t\t\t\tcontainer_begin_destroy(container);\n\t\t\t\tcontainer = parent;\n\t\t\t}\n\t\t}\n\t}\n\n\t// We could be working with a container OR a workspace. These are different\n\t// structures, so we set up pointers to they layouts so we can refer them in\n\t// an abstract way.\n\tenum sway_container_layout new_layout = L_NONE;\n\tenum sway_container_layout old_layout = L_NONE;\n\tif (container) {\n\t\told_layout = container->pending.layout;\n\t\tnew_layout = get_layout(argc, argv,\n\t\t\t\tcontainer->pending.layout, container->prev_split_layout,\n\t\t\t\tcontainer->pending.workspace->output);\n\t} else {\n\t\told_layout = workspace->layout;\n\t\tnew_layout = get_layout(argc, argv,\n\t\t\t\tworkspace->layout, workspace->prev_split_layout,\n\t\t\t\tworkspace->output);\n\t}\n\tif (new_layout == L_NONE) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\tif (new_layout != old_layout) {\n\t\tif (container) {\n\t\t\tif (old_layout != L_TABBED && old_layout != L_STACKED) {\n\t\t\t\tcontainer->prev_split_layout = old_layout;\n\t\t\t}\n\t\t\tcontainer->pending.layout = new_layout;\n\t\t\tcontainer_update_representation(container);\n\t\t} else if (config->handler_context.container) {\n\t\t\t// i3 avoids changing workspace layouts with a new container\n\t\t\t// https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817\n\t\t\tcontainer = workspace_wrap_children(workspace);\n\t\t\tcontainer->pending.layout = new_layout;\n\t\t\tcontainer_update_representation(container);\n\t\t} else {\n\t\t\tif (old_layout != L_TABBED && old_layout != L_STACKED) {\n\t\t\t\tworkspace->prev_split_layout = old_layout;\n\t\t\t}\n\t\t\tworkspace->layout = new_layout;\n\t\t\tworkspace_update_representation(workspace);\n\t\t}\n\t\tif (root->fullscreen_global) {\n\t\t\tarrange_root();\n\t\t} else {\n\t\t\tarrange_workspace(workspace);\n\t\t}\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/mark.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/view.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\n// mark foo                      Same as mark --replace foo\n// mark --add foo                Add this mark to view's list\n// mark --replace foo            Replace view's marks with this single one\n// mark --add --toggle foo       Toggle current mark and persist other marks\n// mark --replace --toggle foo   Toggle current mark and remove other marks\n\nstruct cmd_results *cmd_mark(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"mark\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Only containers can have marks\");\n\t}\n\n\tbool add = false, toggle = false;\n\twhile (argc > 0 && has_prefix(*argv, \"--\")) {\n\t\tif (strcmp(*argv, \"--add\") == 0) {\n\t\t\tadd = true;\n\t\t} else if (strcmp(*argv, \"--replace\") == 0) {\n\t\t\tadd = false;\n\t\t} else if (strcmp(*argv, \"--toggle\") == 0) {\n\t\t\ttoggle = true;\n\t\t} else {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Unrecognized argument '%s'\", *argv);\n\t\t}\n\t\t++argv;\n\t\t--argc;\n\t}\n\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected '[--add|--replace] [--toggle] <identifier>'\");\n\t}\n\n\tchar *mark = join_args(argv, argc);\n\tbool had_mark = container_has_mark(container, mark);\n\n\tif (!add) {\n\t\t// Replacing\n\t\tcontainer_clear_marks(container);\n\t}\n\n\tcontainer_find_and_unmark(mark);\n\n\tif (!toggle || !had_mark) {\n\t\tcontainer_add_mark(container, mark);\n\t}\n\n\tfree(mark);\n\tcontainer_update_marks(container);\n\tif (container->view) {\n\t\tview_execute_criteria(container->view);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/max_render_time.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/view.h\"\n\nstruct cmd_results *cmd_max_render_time(int argc, char **argv) {\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing max render time argument.\");\n\t}\n\n\tint max_render_time;\n\tif (!strcmp(*argv, \"off\")) {\n\t\tmax_render_time = 0;\n\t} else {\n\t\tchar *end;\n\t\tmax_render_time = strtol(*argv, &end, 10);\n\t\tif (*end || max_render_time <= 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid max render time.\");\n\t\t}\n\t}\n\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container || !container->view) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Only views can have a max_render_time\");\n\t}\n\n\tstruct sway_view *view = container->view;\n\tview->max_render_time = max_render_time;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/mode.c",
    "content": "#include <stdbool.h>\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/ipc-server.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\n// Must be in order for the bsearch\nstatic const struct cmd_handler mode_handlers[] = {\n\t{ \"bindcode\", cmd_bindcode },\n\t{ \"bindgesture\", cmd_bindgesture },\n\t{ \"bindswitch\", cmd_bindswitch },\n\t{ \"bindsym\", cmd_bindsym },\n\t{ \"set\", cmd_set },\n\t{ \"unbindcode\", cmd_unbindcode },\n\t{ \"unbindgesture\", cmd_unbindgesture },\n\t{ \"unbindswitch\", cmd_unbindswitch },\n\t{ \"unbindsym\", cmd_unbindsym },\n};\n\nstruct cmd_results *cmd_mode(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"mode\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tbool pango = strcmp(*argv, \"--pango_markup\") == 0;\n\tif (pango) {\n\t\targc--; argv++;\n\t\tif (argc == 0) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Mode name is missing\");\n\t\t}\n\t}\n\n\tif (config->reading && argc == 1) {\n\t\treturn cmd_results_new(CMD_DEFER, NULL);\n\t}\n\n\tchar *mode_name = *argv;\n\tstrip_quotes(mode_name);\n\tstruct sway_mode *mode = NULL;\n\t// Find mode\n\tfor (int i = 0; i < config->modes->length; ++i) {\n\t\tstruct sway_mode *test = config->modes->items[i];\n\t\tif (strcmp(test->name, mode_name) == 0) {\n\t\t\tmode = test;\n\t\t\tbreak;\n\t\t}\n\t}\n\t// Create mode if it doesn't exist\n\tif (!mode && argc > 1) {\n\t\tmode = calloc(1, sizeof(struct sway_mode));\n\t\tif (!mode) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate mode\");\n\t\t}\n\t\tmode->name = strdup(mode_name);\n\t\tmode->keysym_bindings = create_list();\n\t\tmode->keycode_bindings = create_list();\n\t\tmode->mouse_bindings = create_list();\n\t\tmode->switch_bindings = create_list();\n\t\tmode->gesture_bindings = create_list();\n\t\tmode->pango = pango;\n\t\tlist_add(config->modes, mode);\n\t}\n\tif (!mode) {\n\t\terror = cmd_results_new(CMD_INVALID, \"Unknown mode `%s'\", mode_name);\n\t\treturn error;\n\t}\n\t// Set current mode\n\tstruct sway_mode *stored_mode = config->current_mode;\n\tconfig->current_mode = mode;\n\tif (argc == 1) {\n\t\t// trigger IPC mode event\n\t\tsway_log(SWAY_DEBUG, \"Switching to mode `%s' (pango=%d)\",\n\t\t\t\tmode->name, mode->pango);\n\t\tipc_event_mode(config->current_mode->name,\n\t\t\t\tconfig->current_mode->pango);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\t// Create binding\n\tstruct cmd_results *result = config_subcommand(argv + 1, argc - 1,\n\t\t\tmode_handlers, sizeof(mode_handlers));\n\tconfig->current_mode = stored_mode;\n\n\treturn result;\n}\n"
  },
  {
    "path": "sway/commands/mouse_warping.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n\nstruct cmd_results *cmd_mouse_warping(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"mouse_warping\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t} else if (strcasecmp(argv[0], \"container\") == 0) {\n\t\tconfig->mouse_warping = WARP_CONTAINER;\n\t} else if (strcasecmp(argv[0], \"output\") == 0) {\n\t\tconfig->mouse_warping = WARP_OUTPUT;\n\t} else if (strcasecmp(argv[0], \"none\") == 0) {\n\t\tconfig->mouse_warping = WARP_NO;\n\t} else {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Expected 'mouse_warping output|container|none'\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n"
  },
  {
    "path": "sway/commands/move.c",
    "content": "#include <ctype.h>\n#include <math.h>\n#include <stdbool.h>\n#include <string.h>\n#include <strings.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_output_layout.h>\n#include \"sway/commands.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/workspace.h\"\n#include \"stringop.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstatic const char expected_syntax[] =\n\t\"Expected 'move <left|right|up|down> <[px] px>' or \"\n\t\"'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or \"\n\t\"'move <container|window|workspace> [to] output <name|direction>' or \"\n\t\"'move <container|window> [to] mark <mark>'\";\n\nstatic struct sway_output *output_in_direction(const char *direction_string,\n\t\tstruct sway_output *reference, int ref_lx, int ref_ly) {\n\tif (strcasecmp(direction_string, \"current\") == 0) {\n\t\tstruct sway_workspace *active_ws =\n\t\t\tseat_get_focused_workspace(config->handler_context.seat);\n\t\tif (!active_ws) {\n\t\t\treturn NULL;\n\t\t}\n\t\treturn active_ws->output;\n\t}\n\n\tstruct {\n\t\tchar *name;\n\t\tenum wlr_direction direction;\n\t} names[] = {\n\t\t{ \"up\", WLR_DIRECTION_UP },\n\t\t{ \"down\", WLR_DIRECTION_DOWN },\n\t\t{ \"left\", WLR_DIRECTION_LEFT },\n\t\t{ \"right\", WLR_DIRECTION_RIGHT },\n\t};\n\n\tenum wlr_direction direction = 0;\n\n\tfor (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {\n\t\tif (strcasecmp(names[i].name, direction_string) == 0) {\n\t\t\tdirection = names[i].direction;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (reference && direction) {\n\t\tstruct wlr_output *target = wlr_output_layout_adjacent_output(\n\t\t\t\troot->output_layout, direction, reference->wlr_output,\n\t\t\t\tref_lx, ref_ly);\n\n\t\tif (!target) {\n\t\t\ttarget = wlr_output_layout_farthest_output(\n\t\t\t\t\troot->output_layout, opposite_direction(direction),\n\t\t\t\t\treference->wlr_output, ref_lx, ref_ly);\n\t\t}\n\n\t\tif (target) {\n\t\t\treturn target->data;\n\t\t}\n\t}\n\n\treturn output_by_name_or_id(direction_string);\n}\n\nstatic bool is_parallel(enum sway_container_layout layout,\n\t\tenum wlr_direction dir) {\n\tswitch (layout) {\n\tcase L_TABBED:\n\tcase L_HORIZ:\n\t\treturn dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT;\n\tcase L_STACKED:\n\tcase L_VERT:\n\t\treturn dir == WLR_DIRECTION_UP || dir == WLR_DIRECTION_DOWN;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n/**\n * Ensures all seats focus the fullscreen container if needed.\n */\nstatic void workspace_focus_fullscreen(struct sway_workspace *workspace) {\n\tif (!workspace->fullscreen) {\n\t\treturn;\n\t}\n\tstruct sway_seat *seat;\n\tstruct sway_workspace *focus_ws;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tfocus_ws = seat_get_focused_workspace(seat);\n\t\tif (focus_ws == workspace) {\n\t\t\tstruct sway_node *new_focus =\n\t\t\t\tseat_get_focus_inactive(seat, &workspace->fullscreen->node);\n\t\t\tseat_set_raw_focus(seat, new_focus);\n\t\t}\n\t}\n}\n\nstatic void container_move_to_container_from_direction(\n\t\tstruct sway_container *container, struct sway_container *destination,\n\t\tenum wlr_direction move_dir) {\n\tif (destination->view) {\n\t\tif (destination->pending.parent == container->pending.parent &&\n\t\t\t\tdestination->pending.workspace == container->pending.workspace) {\n\t\t\tsway_log(SWAY_DEBUG, \"Swapping siblings\");\n\t\t\tlist_t *siblings = container_get_siblings(container);\n\t\t\tint container_index = list_find(siblings, container);\n\t\t\tint destination_index = list_find(siblings, destination);\n\t\t\tlist_swap(siblings, container_index, destination_index);\n\t\t\tcontainer_update_representation(container);\n\t\t} else {\n\t\t\tsway_log(SWAY_DEBUG, \"Promoting to sibling of cousin\");\n\t\t\tint offset =\n\t\t\t\tmove_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP;\n\t\t\tint index = container_sibling_index(destination) + offset;\n\t\t\tif (destination->pending.parent) {\n\t\t\t\tcontainer_insert_child(destination->pending.parent, container, index);\n\t\t\t} else {\n\t\t\t\tworkspace_insert_tiling(destination->pending.workspace,\n\t\t\t\t\t\tcontainer, index);\n\t\t\t}\n\t\t\tcontainer->pending.width = container->pending.height = 0;\n\t\t\tcontainer->width_fraction = container->height_fraction = 0;\n\t\t\tworkspace_squash(destination->pending.workspace);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (is_parallel(destination->pending.layout, move_dir)) {\n\t\tsway_log(SWAY_DEBUG, \"Reparenting container (parallel)\");\n\t\tint index =\n\t\t\tmove_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ?\n\t\t\t0 : destination->pending.children->length;\n\t\tcontainer_insert_child(destination, container, index);\n\t\tcontainer->pending.width = container->pending.height = 0;\n\t\tcontainer->width_fraction = container->height_fraction = 0;\n\t\tworkspace_squash(destination->pending.workspace);\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Reparenting container (perpendicular)\");\n\tstruct sway_node *focus_inactive = seat_get_active_tiling_child(\n\t\t\tconfig->handler_context.seat, &destination->node);\n\tif (!focus_inactive || focus_inactive == &destination->node) {\n\t\t// The container has no children\n\t\tcontainer_add_child(destination, container);\n\t\treturn;\n\t}\n\n\t// Try again but with the child\n\tcontainer_move_to_container_from_direction(container,\n\t\t\tfocus_inactive->sway_container, move_dir);\n}\n\nstatic void container_move_to_workspace_from_direction(\n\t\tstruct sway_container *container, struct sway_workspace *workspace,\n\t\tenum wlr_direction move_dir) {\n\tcontainer->pending.width = container->pending.height = 0;\n\tcontainer->width_fraction = container->height_fraction = 0;\n\n\tif (is_parallel(workspace->layout, move_dir)) {\n\t\tsway_log(SWAY_DEBUG, \"Reparenting container (parallel)\");\n\t\tint index =\n\t\t\tmove_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ?\n\t\t\t0 : workspace->tiling->length;\n\t\tworkspace_insert_tiling(workspace, container, index);\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Reparenting container (perpendicular)\");\n\tstruct sway_container *focus_inactive = seat_get_focus_inactive_tiling(\n\t\t\tconfig->handler_context.seat, workspace);\n\tif (!focus_inactive) {\n\t\t// The workspace has no tiling children\n\t\tworkspace_add_tiling(workspace, container);\n\t\treturn;\n\t}\n\twhile (focus_inactive->pending.parent) {\n\t\tfocus_inactive = focus_inactive->pending.parent;\n\t}\n\tcontainer_move_to_container_from_direction(container, focus_inactive,\n\t\t\tmove_dir);\n}\n\nstatic void container_move_to_workspace(struct sway_container *container,\n\t\tstruct sway_workspace *workspace) {\n\tif (container->pending.workspace == workspace) {\n\t\treturn;\n\t}\n\tstruct sway_workspace *old_workspace = container->pending.workspace;\n\tif (container_is_floating(container)) {\n\t\tstruct sway_output *old_output = container->pending.workspace->output;\n\t\tcontainer_detach(container);\n\t\tworkspace_add_floating(workspace, container);\n\t\tcontainer_handle_fullscreen_reparent(container);\n\t\t// If changing output, adjust the coordinates of the window.\n\t\tif (old_output != workspace->output && !container->pending.fullscreen_mode) {\n\t\t\tstruct wlr_box workspace_box, old_workspace_box;\n\t\t\tworkspace_get_box(workspace, &workspace_box);\n\t\t\tworkspace_get_box(old_workspace, &old_workspace_box);\n\t\t\tfloating_fix_coordinates(container, &old_workspace_box, &workspace_box);\n\t\t\tif (container->scratchpad && workspace->output) {\n\t\t\t\tstruct wlr_box output_box;\n\t\t\t\toutput_get_box(workspace->output, &output_box);\n\t\t\t\tcontainer->transform = workspace_box;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tcontainer_detach(container);\n\t\tif (workspace_is_empty(workspace) && container->pending.children) {\n\t\t\tworkspace_unwrap_children(workspace, container);\n\t\t\tcontainer_reap_empty(container);\n\t\t} else {\n\t\t\tcontainer->pending.width = container->pending.height = 0;\n\t\t\tcontainer->width_fraction = container->height_fraction = 0;\n\t\t\tworkspace_add_tiling(workspace, container);\n\t\t}\n\t\tcontainer_update_representation(container);\n\t}\n\tif (container->view) {\n\t\tipc_event_window(container, \"move\");\n\t}\n\tworkspace_detect_urgent(old_workspace);\n\tworkspace_detect_urgent(workspace);\n\tworkspace_focus_fullscreen(workspace);\n}\n\nstatic void container_move_to_container(struct sway_container *container,\n\t\tstruct sway_container *destination) {\n\tif (container == destination\n\t\t\t|| container_has_ancestor(destination, container)) {\n\t\treturn;\n\t}\n\tif (container_is_floating(container)) {\n\t\tcontainer_move_to_workspace(container, destination->pending.workspace);\n\t\treturn;\n\t}\n\tstruct sway_workspace *old_workspace = container->pending.workspace;\n\n\tcontainer_detach(container);\n\tcontainer->pending.width = container->pending.height = 0;\n\tcontainer->width_fraction = container->height_fraction = 0;\n\n\tif (destination->view) {\n\t\tcontainer_add_sibling(destination, container, 1);\n\t} else {\n\t\tcontainer_add_child(destination, container);\n\t}\n\n\tif (container->view) {\n\t\tipc_event_window(container, \"move\");\n\t}\n\n\tif (destination->pending.workspace) {\n\t\tworkspace_focus_fullscreen(destination->pending.workspace);\n\t\tworkspace_detect_urgent(destination->pending.workspace);\n\t}\n\n\tif (old_workspace && old_workspace != destination->pending.workspace) {\n\t\tworkspace_detect_urgent(old_workspace);\n\t}\n}\n\nstatic bool container_move_to_next_output(struct sway_container *container,\n\t\tstruct sway_output *output, enum wlr_direction move_dir) {\n\tstruct sway_output *next_output =\n\t\toutput_get_in_direction(output, move_dir);\n\tif (next_output) {\n\t\tstruct sway_workspace *ws = output_get_active_workspace(next_output);\n\t\tif (!sway_assert(ws, \"Expected output to have a workspace\")) {\n\t\t\treturn false;\n\t\t}\n\t\tswitch (container->pending.fullscreen_mode) {\n\t\tcase FULLSCREEN_NONE:\n\t\t\tcontainer_move_to_workspace_from_direction(container, ws, move_dir);\n\t\t\treturn true;\n\t\tcase FULLSCREEN_WORKSPACE:\n\t\t\tcontainer_move_to_workspace(container, ws);\n\t\t\treturn true;\n\t\tcase FULLSCREEN_GLOBAL:\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\n// Returns true if moved\nstatic bool container_move_in_direction(struct sway_container *container,\n\t\tenum wlr_direction move_dir) {\n\t// If moving a fullscreen view, only consider outputs\n\tswitch (container->pending.fullscreen_mode) {\n\tcase FULLSCREEN_NONE:\n\t\tbreak;\n\tcase FULLSCREEN_WORKSPACE:\n\t\treturn container_move_to_next_output(container,\n\t\t\t\tcontainer->pending.workspace->output, move_dir);\n\tcase FULLSCREEN_GLOBAL:\n\t\treturn false;\n\t}\n\n\tint offs =\n\t\tmove_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? -1 : 1;\n\tint index = -1;\n\tint\tdesired = -1;\n\tlist_t *siblings = NULL;\n\tstruct sway_container *target = NULL;\n\n\t// Look for a suitable ancestor of the container to move within\n\tstruct sway_container *ancestor = NULL;\n\tstruct sway_container *current = container;\n\tbool wrapped = false;\n\twhile (!ancestor) {\n\t\t// Don't allow containers to move out of their\n\t\t// fullscreen or floating parent\n\t\tif (current->pending.fullscreen_mode || container_is_floating(current)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tenum sway_container_layout parent_layout = container_parent_layout(current);\n\t\tif (!is_parallel(parent_layout, move_dir)) {\n\t\t\tif (!current->pending.parent) {\n\t\t\t\t// No parallel parent, so we reorient the workspace\n\t\t\t\tcurrent = workspace_wrap_children(current->pending.workspace);\n\t\t\t\tcurrent->pending.workspace->layout =\n\t\t\t\t\tmove_dir == WLR_DIRECTION_LEFT ||\n\t\t\t\t\tmove_dir == WLR_DIRECTION_RIGHT ?\n\t\t\t\t\tL_HORIZ : L_VERT;\n\t\t\t\tcontainer->pending.height = container->pending.width = 0;\n\t\t\t\tcontainer->height_fraction = container->width_fraction = 0;\n\t\t\t\tworkspace_update_representation(current->pending.workspace);\n\t\t\t\twrapped = true;\n\t\t\t} else {\n\t\t\t\t// Keep looking for a parallel parent\n\t\t\t\tcurrent = current->pending.parent;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Only scratchpad hidden containers don't have siblings\n\t\t// so siblings != NULL here\n\t\tsiblings = container_get_siblings(current);\n\t\tindex = list_find(siblings, current);\n\t\tdesired = index + offs;\n\t\ttarget = desired == -1 || desired == siblings->length ?\n\t\t\t\tNULL : siblings->items[desired];\n\n\t\t// If the move is simple we can complete it here early\n\t\tif (current == container) {\n\t\t\tif (target) {\n\t\t\t\t// Container will swap with or descend into its neighbor\n\t\t\t\tcontainer_move_to_container_from_direction(container,\n\t\t\t\t\t\ttarget, move_dir);\n\t\t\t\treturn true;\n\t\t\t} else if (!container->pending.parent) {\n\t\t\t\t// Container is at workspace level so we move it to the\n\t\t\t\t// next workspace if possible\n\t\t\t\treturn container_move_to_next_output(container,\n\t\t\t\t\t\tcurrent->pending.workspace->output, move_dir);\n\t\t\t} else {\n\t\t\t\t// Container has escaped its immediate parallel parent\n\t\t\t\tcurrent = current->pending.parent;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// We found a suitable ancestor, the loop will end\n\t\tancestor = current;\n\t}\n\n\tif (target) {\n\t\t// Container will move in with its cousin\n\t\tcontainer_move_to_container_from_direction(container,\n\t\t\t\ttarget, move_dir);\n\t\treturn true;\n\t} else if (!wrapped && !container->pending.parent->pending.parent &&\n\t\t\tcontainer->pending.parent->pending.children->length == 1) {\n\t\t// Treat singleton children as if they are at workspace level like i3\n\t\t// https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367\n\t\treturn container_move_to_next_output(container,\n\t\t\t\tancestor->pending.workspace->output, move_dir);\n\t} else {\n\t\t// Container will be promoted\n\t\tstruct sway_container *old_parent = container->pending.parent;\n\t\tif (ancestor->pending.parent) {\n\t\t\t// Container will move in with its parent\n\t\t\tcontainer_insert_child(ancestor->pending.parent, container,\n\t\t\t\t\tindex + (offs < 0 ? 0 : 1));\n\t\t} else {\n\t\t\t// Container will move to workspace level,\n\t\t\t// may be re-split by workspace_layout\n\t\t\tworkspace_insert_tiling(ancestor->pending.workspace, container,\n\t\t\t\t\tindex + (offs < 0 ? 0 : 1));\n\t\t}\n\t\tancestor->pending.height = ancestor->pending.width = 0;\n\t\tancestor->height_fraction = ancestor->width_fraction = 0;\n\t\tif (old_parent) {\n\t\t\tcontainer_reap_empty(old_parent);\n\t\t}\n\t\tworkspace_squash(container->pending.workspace);\n\t\treturn true;\n\t}\n}\n\nstatic struct cmd_results *cmd_move_to_scratchpad(void);\n\nstatic struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,\n\t\tint argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"move container/window\",\n\t\t\t\tEXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_node *node = config->handler_context.node;\n\tstruct sway_workspace *workspace = config->handler_context.workspace;\n\tstruct sway_container *container = config->handler_context.container;\n\tif (node->type == N_WORKSPACE) {\n\t\tif (workspace->tiling->length == 0) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Can't move an empty workspace\");\n\t\t}\n\t\tcontainer = workspace_wrap_children(workspace);\n\t}\n\n\tif (container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Can't move fullscreen global container\");\n\t}\n\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tstruct sway_container *old_parent = container->pending.parent;\n\tstruct sway_workspace *old_ws = container->pending.workspace;\n\tstruct sway_output *old_output = old_ws ? old_ws->output : NULL;\n\tstruct sway_node *destination = NULL;\n\n\t// determine destination\n\tif (strcasecmp(argv[0], \"workspace\") == 0) {\n\t\t// move container to workspace x\n\t\tstruct sway_workspace *ws = NULL;\n\t\tchar *ws_name = NULL;\n\t\tif (strcasecmp(argv[1], \"next\") == 0 ||\n\t\t\t\tstrcasecmp(argv[1], \"prev\") == 0 ||\n\t\t\t\tstrcasecmp(argv[1], \"next_on_output\") == 0 ||\n\t\t\t\tstrcasecmp(argv[1], \"prev_on_output\") == 0 ||\n\t\t\t\tstrcasecmp(argv[1], \"current\") == 0) {\n\t\t\tws = workspace_by_name(argv[1]);\n\t\t} else if (strcasecmp(argv[1], \"back_and_forth\") == 0) {\n\t\t\tif (!(ws = workspace_by_name(argv[1]))) {\n\t\t\t\tif (seat->prev_workspace_name) {\n\t\t\t\t\tws_name = strdup(seat->prev_workspace_name);\n\t\t\t\t} else {\n\t\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\t\"No workspace was previously active.\");\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (strcasecmp(argv[1], \"number\") == 0) {\n\t\t\t\t// move [window|container] [to] \"workspace number x\"\n\t\t\t\tif (argc < 3) {\n\t\t\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t\t\t\t}\n\t\t\t\tif (!isdigit(argv[2][0])) {\n\t\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\t\"Invalid workspace number '%s'\", argv[2]);\n\t\t\t\t}\n\t\t\t\tws_name = join_args(argv + 2, argc - 2);\n\t\t\t\tws = workspace_by_number(ws_name);\n\t\t\t} else {\n\t\t\t\tws_name = join_args(argv + 1, argc - 1);\n\t\t\t\tws = workspace_by_name(ws_name);\n\t\t\t}\n\n\t\t\tif (!no_auto_back_and_forth && config->auto_back_and_forth &&\n\t\t\t\t\tseat->prev_workspace_name) {\n\t\t\t\t// auto back and forth move\n\t\t\t\tif (old_ws && old_ws->name &&\n\t\t\t\t\t\tstrcmp(old_ws->name, ws_name) == 0) {\n\t\t\t\t\t// if target workspace is the current one\n\t\t\t\t\tfree(ws_name);\n\t\t\t\t\tws_name = strdup(seat->prev_workspace_name);\n\t\t\t\t\tws = workspace_by_name(ws_name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!ws) {\n\t\t\t// We have to create the workspace, but if the container is\n\t\t\t// sticky and the workspace is going to be created on the same\n\t\t\t// output, we'll bail out first.\n\t\t\tif (container_is_sticky_or_child(container)) {\n\t\t\t\tstruct sway_output *new_output =\n\t\t\t\t\tworkspace_get_initial_output(ws_name);\n\t\t\t\tif (old_output == new_output) {\n\t\t\t\t\tfree(ws_name);\n\t\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\t\"Can't move sticky container to another workspace \"\n\t\t\t\t\t\t\t\"on the same output\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tws = workspace_create(NULL, ws_name);\n\t\t\tarrange_workspace(ws);\n\t\t}\n\t\tfree(ws_name);\n\t\tstruct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws);\n\t\tdestination = dst ? &dst->node : &ws->node;\n\t} else if (strcasecmp(argv[0], \"output\") == 0) {\n\t\tstruct sway_output *new_output = output_in_direction(argv[1],\n\t\t\t\told_output, container->pending.x, container->pending.y);\n\t\tif (!new_output) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Can't find output with name/direction '%s'\", argv[1]);\n\t\t}\n\t\tdestination = seat_get_focus_inactive(seat, &new_output->node);\n\t} else if (strcasecmp(argv[0], \"mark\") == 0) {\n\t\tstruct sway_container *dest_con = container_find_mark(argv[1]);\n\t\tif (dest_con == NULL) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Mark '%s' not found\", argv[1]);\n\t\t}\n\t\tdestination = &dest_con->node;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\n\tif (destination->type == N_CONTAINER &&\n\t\t\tcontainer_is_scratchpad_hidden(destination->sway_container)) {\n\t\treturn cmd_move_to_scratchpad();\n\t}\n\n\tif (container_is_sticky_or_child(container) && old_output &&\n\t\t\tnode_has_ancestor(destination, &old_output->node)) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Can't move sticky \"\n\t\t\t\t\"container to another workspace on the same output\");\n\t}\n\n\tstruct sway_output *new_output = node_get_output(destination);\n\tstruct sway_workspace *new_output_last_ws = NULL;\n\tif (new_output && old_output != new_output) {\n\t\tnew_output_last_ws = output_get_active_workspace(new_output);\n\t}\n\n\t// save focus, in case it needs to be restored\n\tstruct sway_node *focus = seat_get_focus(seat);\n\n\t// move container\n\tif (container_is_scratchpad_hidden_or_child(container)) {\n\t\tcontainer_detach(container);\n\t\troot_scratchpad_show(container);\n\t}\n\tswitch (destination->type) {\n\tcase N_WORKSPACE:\n\t\tcontainer_move_to_workspace(container, destination->sway_workspace);\n\t\tbreak;\n\tcase N_OUTPUT: {\n\t\t\tstruct sway_output *output = destination->sway_output;\n\t\t\tstruct sway_workspace *ws = output_get_active_workspace(output);\n\t\t\tif (!sway_assert(ws, \"Expected output to have a workspace\")) {\n\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\"Expected output to have a workspace\");\n\t\t\t}\n\t\t\tcontainer_move_to_workspace(container, ws);\n\t\t}\n\t\tbreak;\n\tcase N_CONTAINER:\n\t\tcontainer_move_to_container(container, destination->sway_container);\n\t\tbreak;\n\tcase N_ROOT:\n\t\tbreak;\n\t}\n\n\t// restore focus on destination output back to its last active workspace\n\tstruct sway_workspace *new_workspace = new_output ?\n\t\toutput_get_active_workspace(new_output) : NULL;\n\tif (new_output &&\n\t\t\t!sway_assert(new_workspace, \"Expected output to have a workspace\")) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Expected output to have a workspace\");\n\t}\n\n\tif (new_output_last_ws && new_output_last_ws != new_workspace) {\n\t\tstruct sway_node *new_output_last_focus =\n\t\t\tseat_get_focus_inactive(seat, &new_output_last_ws->node);\n\t\tseat_set_raw_focus(seat, new_output_last_focus);\n\t}\n\n\t// restore focus\n\tif (focus == &container->node) {\n\t\tfocus = NULL;\n\t\tif (old_parent) {\n\t\t\tfocus = seat_get_focus_inactive(seat, &old_parent->node);\n\t\t}\n\t\tif (!focus && old_ws) {\n\t\t\tfocus = seat_get_focus_inactive(seat, &old_ws->node);\n\t\t}\n\t}\n\tseat_set_focus(seat, focus);\n\n\t// clean-up, destroying parents if the container was the last child\n\tif (old_parent) {\n\t\tcontainer_reap_empty(old_parent);\n\t} else if (old_ws) {\n\t\tworkspace_consider_destroy(old_ws);\n\t}\n\n\t// arrange windows\n\tif (root->fullscreen_global) {\n\t\tarrange_root();\n\t} else {\n\t\tif (old_ws && !old_ws->node.destroying) {\n\t\t\tarrange_workspace(old_ws);\n\t\t}\n\t\tarrange_node(node_get_parent(destination));\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic void workspace_move_to_output(struct sway_workspace *workspace,\n\t\tstruct sway_output *output) {\n\tif (workspace->output == output) {\n\t\treturn;\n\t}\n\tstruct sway_output *old_output = workspace->output;\n\tworkspace_detach(workspace);\n\tstruct sway_workspace *new_output_old_ws =\n\t\toutput_get_active_workspace(output);\n\tif (!sway_assert(new_output_old_ws, \"Expected output to have a workspace\")) {\n\t\treturn;\n\t}\n\n\toutput_add_workspace(output, workspace);\n\n\t// If moving the last workspace from the old output, create a new workspace\n\t// on the old output\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tif (old_output->workspaces->length == 0) {\n\t\tchar *ws_name = workspace_next_name(old_output->wlr_output->name);\n\t\tstruct sway_workspace *ws = workspace_create(old_output, ws_name);\n\t\tfree(ws_name);\n\t\tseat_set_raw_focus(seat, &ws->node);\n\t}\n\n\tworkspace_consider_destroy(new_output_old_ws);\n\n\toutput_sort_workspaces(output);\n\tstruct sway_node *focus = seat_get_focus_inactive(seat, &workspace->node);\n\tseat_set_focus(seat, focus);\n\tworkspace_output_raise_priority(workspace, old_output, output);\n\tipc_event_workspace(NULL, workspace, \"move\");\n}\n\nstatic struct cmd_results *cmd_move_workspace(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"move workspace\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcasecmp(argv[0], \"output\") == 0) {\n\t\t--argc; ++argv;\n\t}\n\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'move workspace to [output] <output>'\");\n\t}\n\n\tstruct sway_workspace *workspace = config->handler_context.workspace;\n\tif (!workspace) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No workspace to move\");\n\t}\n\n\tstruct sway_output *old_output = workspace->output;\n\tint center_x = workspace->width / 2 + workspace->x,\n\t\tcenter_y = workspace->height / 2 + workspace->y;\n\tstruct sway_output *new_output = output_in_direction(argv[0],\n\t\t\told_output, center_x, center_y);\n\tif (!new_output) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\"Can't find output with name/direction '%s'\", argv[0]);\n\t}\n\tworkspace_move_to_output(workspace, new_output);\n\n\tarrange_output(old_output);\n\tarrange_output(new_output);\n\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tseat_consider_warp_to_focus(seat);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *cmd_move_in_direction(\n\t\tenum wlr_direction direction, int argc, char **argv) {\n\tint move_amt = 10;\n\tif (argc) {\n\t\tchar *inv;\n\t\tmove_amt = (int)strtol(argv[0], &inv, 10);\n\t\tif (*inv != '\\0' && strcasecmp(inv, \"px\") != 0) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid distance specified\");\n\t\t}\n\t}\n\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Cannot move workspaces in a direction\");\n\t}\n\tif (container_is_floating(container)) {\n\t\tif (container->pending.fullscreen_mode) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Cannot move fullscreen floating container\");\n\t\t}\n\t\tdouble lx = container->pending.x;\n\t\tdouble ly = container->pending.y;\n\t\tswitch (direction) {\n\t\tcase WLR_DIRECTION_LEFT:\n\t\t\tlx -= move_amt;\n\t\t\tbreak;\n\t\tcase WLR_DIRECTION_RIGHT:\n\t\t\tlx += move_amt;\n\t\t\tbreak;\n\t\tcase WLR_DIRECTION_UP:\n\t\t\tly -= move_amt;\n\t\t\tbreak;\n\t\tcase WLR_DIRECTION_DOWN:\n\t\t\tly += move_amt;\n\t\t\tbreak;\n\t\t}\n\t\tcontainer_floating_move_to(container, lx, ly);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\tstruct sway_workspace *old_ws = container->pending.workspace;\n\tstruct sway_container *old_parent = container->pending.parent;\n\n\tif (!container_move_in_direction(container, direction)) {\n\t\t// Container didn't move\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\t// clean-up, destroying parents if the container was the last child\n\tif (old_parent) {\n\t\tcontainer_reap_empty(old_parent);\n\t} else if (old_ws) {\n\t\tworkspace_consider_destroy(old_ws);\n\t}\n\n\tstruct sway_workspace *new_ws = container->pending.workspace;\n\n\tif (root->fullscreen_global) {\n\t\tarrange_root();\n\t} else {\n\t\tarrange_workspace(old_ws);\n\t\tif (new_ws != old_ws) {\n\t\t\tarrange_workspace(new_ws);\n\t\t}\n\t}\n\n\tif (container->view) {\n\t\tipc_event_window(container, \"move\");\n\t}\n\n\tcontainer_end_mouse_operation(container);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *cmd_move_to_position_pointer(\n\t\tstruct sway_container *container) {\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tif (!seat->cursor) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No cursor device\");\n\t}\n\tstruct wlr_cursor *cursor = seat->cursor->cursor;\n\t/* Determine where to put the window. */\n\tdouble lx = cursor->x - container->pending.width / 2;\n\tdouble ly = cursor->y - container->pending.height / 2;\n\n\t/* Correct target coordinates to be in bounds (on screen). */\n\tstruct wlr_output *output = wlr_output_layout_output_at(\n\t\t\troot->output_layout, cursor->x, cursor->y);\n\tif (output) {\n\t\tstruct wlr_box box;\n\t\twlr_output_layout_get_box(root->output_layout, output, &box);\n\t\tlx = fmax(lx, box.x);\n\t\tly = fmax(ly, box.y);\n\t\tif (lx + container->pending.width > box.x + box.width) {\n\t\t\tlx = box.x + box.width - container->pending.width;\n\t\t}\n\t\tif (ly + container->pending.height > box.y + box.height) {\n\t\t\tly = box.y + box.height - container->pending.height;\n\t\t}\n\t}\n\n\t/* Actually move the container. */\n\tcontainer_floating_move_to(container, lx, ly);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic const char expected_position_syntax[] =\n\t\"Expected 'move [absolute] position <x> [px] <y> [px]' or \"\n\t\"'move [absolute] position center' or \"\n\t\"'move position cursor|mouse|pointer'\";\n\nstatic struct cmd_results *cmd_move_to_position(int argc, char **argv) {\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container || !container_is_floating(container)) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Only floating containers \"\n\t\t\t\t\"can be moved to an absolute position\");\n\t}\n\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_position_syntax);\n\t}\n\n\tbool absolute = false;\n\tif (strcmp(argv[0], \"absolute\") == 0) {\n\t\tabsolute = true;\n\t\t--argc;\n\t\t++argv;\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_position_syntax);\n\t}\n\tif (strcmp(argv[0], \"position\") == 0) {\n\t\t--argc;\n\t\t++argv;\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_position_syntax);\n\t}\n\tif (strcmp(argv[0], \"cursor\") == 0 || strcmp(argv[0], \"mouse\") == 0 ||\n\t\t\tstrcmp(argv[0], \"pointer\") == 0) {\n\t\tif (absolute) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_position_syntax);\n\t\t}\n\t\treturn cmd_move_to_position_pointer(container);\n\t} else if (strcmp(argv[0], \"center\") == 0) {\n\t\tdouble lx, ly;\n\t\tif (absolute) {\n\t\t\tlx = root->x + (root->width - container->pending.width) / 2;\n\t\t\tly = root->y + (root->height - container->pending.height) / 2;\n\t\t} else {\n\t\t\tstruct sway_workspace *ws = container->pending.workspace;\n\t\t\tif (!ws) {\n\t\t\t\tstruct sway_seat *seat = config->handler_context.seat;\n\t\t\t\tws = seat_get_focused_workspace(seat);\n\t\t\t}\n\t\t\tlx = ws->x + (ws->width - container->pending.width) / 2;\n\t\t\tly = ws->y + (ws->height - container->pending.height) / 2;\n\t\t}\n\t\tcontainer_floating_move_to(container, lx, ly);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tif (argc < 2) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"%s\", expected_position_syntax);\n\t}\n\n\tstruct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };\n\t// X direction\n\tint num_consumed_args = parse_movement_amount(argc, argv, &lx);\n\targc -= num_consumed_args;\n\targv += num_consumed_args;\n\tif (lx.unit == MOVEMENT_UNIT_INVALID) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid x position specified\");\n\t}\n\n\tif (argc < 1) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"%s\", expected_position_syntax);\n\t}\n\n\tstruct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };\n\t// Y direction\n\tnum_consumed_args = parse_movement_amount(argc, argv, &ly);\n\targc -= num_consumed_args;\n\targv += num_consumed_args;\n\tif (argc > 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_position_syntax);\n\t}\n\tif (ly.unit == MOVEMENT_UNIT_INVALID) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid y position specified\");\n\t}\n\n\tstruct sway_workspace *ws = container->pending.workspace;\n\tif (!ws) {\n\t\tstruct sway_seat *seat = config->handler_context.seat;\n\t\tws = seat_get_focused_workspace(seat);\n\t}\n\n\tswitch (lx.unit) {\n\tcase MOVEMENT_UNIT_PPT:\n\t\tif (container_is_scratchpad_hidden(container)) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Cannot move a hidden scratchpad container by ppt\");\n\t\t}\n\t\tif (absolute) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Cannot move to absolute positions by ppt\");\n\t\t}\n\t\t// Convert to px\n\t\tlx.amount = ws->width * lx.amount / 100;\n\t\tlx.unit = MOVEMENT_UNIT_PX;\n\t\t// Falls through\n\tcase MOVEMENT_UNIT_PX:\n\tcase MOVEMENT_UNIT_DEFAULT:\n\t\tbreak;\n\tcase MOVEMENT_UNIT_INVALID:\n\t\tsway_assert(false, \"invalid x unit\");\n\t\tbreak;\n\t}\n\n\tswitch (ly.unit) {\n\tcase MOVEMENT_UNIT_PPT:\n\t\tif (container_is_scratchpad_hidden(container)) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Cannot move a hidden scratchpad container by ppt\");\n\t\t}\n\t\tif (absolute) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Cannot move to absolute positions by ppt\");\n\t\t}\n\t\t// Convert to px\n\t\tly.amount = ws->height * ly.amount / 100;\n\t\tly.unit = MOVEMENT_UNIT_PX;\n\t\t// Falls through\n\tcase MOVEMENT_UNIT_PX:\n\tcase MOVEMENT_UNIT_DEFAULT:\n\t\tbreak;\n\tcase MOVEMENT_UNIT_INVALID:\n\t\tsway_assert(false, \"invalid y unit\");\n\t\tbreak;\n\t}\n\tif (!absolute) {\n\t\tlx.amount += ws->x;\n\t\tly.amount += ws->y;\n\t}\n\tcontainer_floating_move_to(container, lx.amount, ly.amount);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *cmd_move_to_scratchpad(void) {\n\tstruct sway_node *node = config->handler_context.node;\n\tstruct sway_container *con = config->handler_context.container;\n\tstruct sway_workspace *ws = config->handler_context.workspace;\n\tif (node->type == N_WORKSPACE && ws->tiling->length == 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't move an empty workspace to the scratchpad\");\n\t}\n\tif (node->type == N_WORKSPACE) {\n\t\t// Wrap the workspace's children in a container\n\t\tcon = workspace_wrap_children(ws);\n\t\tws->layout = L_HORIZ;\n\t}\n\n\t// If the container is in a floating split container,\n\t// operate on the split container instead of the child.\n\tif (container_is_floating_or_child(con)) {\n\t\twhile (con->pending.parent) {\n\t\t\tcon = con->pending.parent;\n\t\t}\n\t}\n\n\tif (!con->scratchpad) {\n\t\troot_scratchpad_add_container(con, NULL);\n\t} else if (con->pending.workspace) {\n\t\troot_scratchpad_hide(con);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic const char expected_full_syntax[] = \"Expected \"\n\t\"'move left|right|up|down [<amount> [px]]'\"\n\t\" or 'move [--no-auto-back-and-forth] [window|container] [to] workspace\"\n\t\"  <name>|next|prev|next_on_output|prev_on_output|current|(number <num>)'\"\n\t\" or 'move [window|container] [to] output <name/id>|left|right|up|down'\"\n\t\" or 'move [window|container] [to] mark <mark>'\"\n\t\" or 'move [window|container] [to] scratchpad'\"\n\t\" or 'move workspace to [output] <name/id>|left|right|up|down'\"\n\t\" or 'move [window|container] [to] [absolute] position <x> [px] <y> [px]'\"\n\t\" or 'move [window|container] [to] [absolute] position center'\"\n\t\" or 'move [window|container] [to] position mouse|cursor|pointer'\";\n\nstruct cmd_results *cmd_move(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"move\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"left\") == 0) {\n\t\treturn cmd_move_in_direction(WLR_DIRECTION_LEFT, --argc, ++argv);\n\t} else if (strcasecmp(argv[0], \"right\") == 0) {\n\t\treturn cmd_move_in_direction(WLR_DIRECTION_RIGHT, --argc, ++argv);\n\t} else if (strcasecmp(argv[0], \"up\") == 0) {\n\t\treturn cmd_move_in_direction(WLR_DIRECTION_UP, --argc, ++argv);\n\t} else if (strcasecmp(argv[0], \"down\") == 0) {\n\t\treturn cmd_move_in_direction(WLR_DIRECTION_DOWN, --argc, ++argv);\n\t} else if (strcasecmp(argv[0], \"workspace\") == 0 && argc >= 2\n\t\t\t&& (strcasecmp(argv[1], \"to\") == 0 ||\n\t\t\t\tstrcasecmp(argv[1], \"output\") == 0)) {\n\t\targc -= 2; argv += 2;\n\t\treturn cmd_move_workspace(argc, argv);\n\t}\n\n\tbool no_auto_back_and_forth = false;\n\tif (strcasecmp(argv[0], \"--no-auto-back-and-forth\") == 0) {\n\t\tno_auto_back_and_forth = true;\n\t\t--argc; ++argv;\n\t}\n\n\tif (argc > 0 && (strcasecmp(argv[0], \"window\") == 0 ||\n\t\t\tstrcasecmp(argv[0], \"container\") == 0)) {\n\t\t--argc; ++argv;\n\t}\n\n\tif (argc > 0 && strcasecmp(argv[0], \"to\") == 0) {\n\t\t--argc;\t++argv;\n\t}\n\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_full_syntax);\n\t}\n\n\t// Only `move [window|container] [to] workspace` supports\n\t// `--no-auto-back-and-forth` so treat others as invalid syntax\n\tif (no_auto_back_and_forth && strcasecmp(argv[0], \"workspace\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_full_syntax);\n\t}\n\n\tif (strcasecmp(argv[0], \"workspace\") == 0 ||\n\t\t\tstrcasecmp(argv[0], \"output\") == 0 ||\n\t\t\tstrcasecmp(argv[0], \"mark\") == 0) {\n\t\treturn cmd_move_container(no_auto_back_and_forth, argc, argv);\n\t} else if (strcasecmp(argv[0], \"scratchpad\") == 0) {\n\t\treturn cmd_move_to_scratchpad();\n\t} else if (strcasecmp(argv[0], \"position\") == 0 ||\n\t\t\t(argc > 1 && strcasecmp(argv[0], \"absolute\") == 0 &&\n\t\t\tstrcasecmp(argv[1], \"position\") == 0)) {\n\t\treturn cmd_move_to_position(argc, argv);\n\t}\n\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_full_syntax);\n}\n"
  },
  {
    "path": "sway/commands/new_float.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *cmd_new_float(int argc, char **argv) {\n\tsway_log(SWAY_INFO, \"Warning: new_float is deprecated. \"\n\t\t\"Use default_floating_border instead.\");\n\tif (config->reading) {\n\t\tconfig_add_swaynag_warning(\"new_float is deprecated. \"\n\t\t\t\"Use default_floating_border instead.\");\n\t}\n\treturn cmd_default_floating_border(argc, argv);\n}\n"
  },
  {
    "path": "sway/commands/new_window.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *cmd_new_window(int argc, char **argv) {\n\tsway_log(SWAY_INFO, \"Warning: new_window is deprecated. \"\n\t\t\"Use default_border instead.\");\n\tif (config->reading) {\n\t\tconfig_add_swaynag_warning(\"new_window is deprecated. \"\n\t\t\t\"Use default_border instead.\");\n\t}\n\treturn cmd_default_border(argc, argv);\n}\n"
  },
  {
    "path": "sway/commands/no_focus.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/criteria.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nstruct cmd_results *cmd_no_focus(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"no_focus\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tchar *err_str = NULL;\n\tstruct criteria *criteria = criteria_parse(argv[0], &err_str);\n\tif (!criteria) {\n\t\terror = cmd_results_new(CMD_INVALID, \"%s\", err_str);\n\t\tfree(err_str);\n\t\treturn error;\n\t}\n\n\n\tcriteria->type = CT_NO_FOCUS;\n\n\t// Check if it already exists\n\tif (criteria_already_exists(criteria)) {\n\t\tsway_log(SWAY_DEBUG, \"no_focus already exists: '%s'\", criteria->raw);\n\t\tcriteria_destroy(criteria);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\n\tlist_add(config->criteria, criteria);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/nop.c",
    "content": "#include \"sway/commands.h\"\n\nstruct cmd_results *cmd_nop(int argc, char **argv) {\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/opacity.c",
    "content": "#include <assert.h>\n#include <stdlib.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/output.h\"\n#include \"log.h\"\n\nstruct cmd_results *cmd_opacity(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"opacity\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_container *con = config->handler_context.container;\n\n\tif (con == NULL) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No current container\");\n\t}\n\n\tchar *err;\n\tfloat val = strtof(argc == 1 ? argv[0] : argv[1], &err);\n\tif (*err) {\n\t\treturn cmd_results_new(CMD_INVALID, \"opacity float invalid\");\n\t}\n\n\tif (!strcasecmp(argv[0], \"plus\")) {\n\t\tval = con->alpha + val;\n\t} else if (!strcasecmp(argv[0], \"minus\")) {\n\t\tval = con->alpha - val;\n\t} else if (argc > 1 && strcasecmp(argv[0], \"set\")) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected: set|plus|minus <0..1>: %s\", argv[0]);\n\t}\n\n\tif (val < 0 || val > 1) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"opacity value out of bounds\");\n\t}\n\n\tcon->alpha = val;\n\toutput_configure_scene(NULL, &con->scene_tree->node, 1);\n\tcontainer_update(con);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/output/adaptive_sync.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"util.h\"\n\nstruct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (argc == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing adaptive_sync argument\");\n\t}\n\n\tbool current_value = true;\n\tif (strcasecmp(argv[0], \"toggle\") == 0) {\n\t\tconst char *oc_name = config->handler_context.output_config->name;\n\t\tif (strcmp(oc_name, \"*\") == 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Cannot apply toggle to all outputs\");\n\t\t}\n\n\t\tstruct sway_output *sway_output = all_output_by_name_or_id(oc_name);\n\t\tif (!sway_output || !sway_output->wlr_output) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Cannot apply toggle to unknown output %s\", oc_name);\n\t\t}\n\n\t\tcurrent_value =\n\t\t\tsway_output->wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED;\n\t}\n\n\tconfig->handler_context.output_config->adaptive_sync = parse_boolean(argv[0], current_value);\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/allow_tearing.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"util.h\"\n\nstruct cmd_results *output_cmd_allow_tearing(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (argc == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing allow_tearing argument\");\n\t}\n\n\tif (parse_boolean(argv[0],\n\t\t\t(config->handler_context.output_config->allow_tearing == 1))) {\n\t\tconfig->handler_context.output_config->allow_tearing = 1;\n\t} else {\n\t\tconfig->handler_context.output_config->allow_tearing = 0;\n\t}\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/background.c",
    "content": "#include <libgen.h>\n#include <stdio.h>\n#include <string.h>\n#include <strings.h>\n#include <unistd.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic const char *bg_options[] = {\n\t\"stretch\",\n\t\"center\",\n\t\"fill\",\n\t\"fit\",\n\t\"tile\",\n};\n\nstatic bool validate_color(const char *color) {\n\tif (strlen(color) != 7 || color[0] != '#') {\n\t\treturn false;\n\t}\n\n\tchar *ptr = NULL;\n\tstrtol(&color[1], &ptr, 16);\n\treturn *ptr == '\\0';\n}\n\nstruct cmd_results *output_cmd_background(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Missing background file or color specification.\");\n\t}\n\tif (argc < 2) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Missing background scaling mode or `solid_color`.\");\n\t}\n\n\tstruct output_config *output = config->handler_context.output_config;\n\tchar *src = NULL;\n\tif (strcasecmp(argv[1], \"solid_color\") == 0) {\n\t\tif (!validate_color(argv[0])) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Colors should be of the form #RRGGBB\");\n\t\t}\n\t\tif (!(output->background = strdup(argv[0]))) goto cleanup;\n\t\tif (!(output->background_option = strdup(\"solid_color\"))) goto cleanup;\n\t\toutput->background_fallback = NULL;\n\t\targc -= 2; argv += 2;\n\t} else {\n\t\tbool valid = false;\n\t\tchar *mode;\n\t\tsize_t j;\n\t\tfor (j = 0; j < (size_t)argc; ++j) {\n\t\t\tmode = argv[j];\n\t\t\tsize_t n = sizeof(bg_options) / sizeof(char *);\n\t\t\tfor (size_t k = 0; k < n; ++k) {\n\t\t\t\tif (strcasecmp(mode, bg_options[k]) == 0) {\n\t\t\t\t\tvalid = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (valid) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!valid) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Missing background scaling mode.\");\n\t\t}\n\t\tif (j == 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Missing background file\");\n\t\t}\n\n\t\tif (!(src = join_args(argv, j))) goto cleanup;\n\t\tif (!expand_path(&src)) {\n\t\t\tstruct cmd_results *cmd_res = cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Invalid syntax (%s)\", src);\n\t\t\tfree(src);\n\t\t\treturn cmd_res;\n\t\t}\n\n\t\tif (config->reading && *src != '/') {\n\t\t\t// src file is inside configuration dir\n\n\t\t\tchar *conf = strdup(config->current_config_path);\n\t\t\tif (!conf) goto cleanup;\n\n\t\t\tchar *conf_path = dirname(conf);\n\t\t\tchar *real_src = malloc(strlen(conf_path) + strlen(src) + 2);\n\t\t\tif (!real_src) {\n\t\t\t\tfree(conf);\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\n\t\t\tsnprintf(real_src, strlen(conf_path) + strlen(src) + 2, \"%s/%s\", conf_path, src);\n\t\t\tfree(src);\n\t\t\tfree(conf);\n\t\t\tsrc = real_src;\n\t\t}\n\n\t\tbool can_access = access(src, F_OK) != -1;\n\t\targc -= j + 1; argv += j + 1;\n\t\tfree(output->background_option);\n\t\tfree(output->background_fallback);\n\t\tfree(output->background);\n\t\toutput->background = output->background_option = output->background_fallback = NULL;\n\t\tchar *fallback = NULL;\n\n\t\tif (argc && *argv[0] == '#') {\n\t\t\tif (validate_color(argv[0])) {\n\t\t\t\tif (!(fallback = strdup(argv[0]))) goto cleanup;\n\t\t\t\toutput->background_fallback = fallback;\n\t\t\t} else {\n\t\t\t\tsway_log(SWAY_ERROR, \"fallback '%s' should be of the form #RRGGBB\", argv[0]);\n\t\t\t\tconfig_add_swaynag_warning(\"fallback '%s' should be of the form #RRGGBB\\n\", argv[0]);\n\t\t\t}\n\t\t\targc--; argv++;\n\t\t}\n\n\t\tif (!can_access) {\n\t\t\tif (!fallback) {\n\t\t\t\tsway_log(SWAY_ERROR, \"Unable to access background file '%s' \"\n\t\t\t\t   \"and no valid fallback provided\", src);\n\t\t\t\tstruct cmd_results *res = cmd_results_new(CMD_FAILURE, \"Unable to access \"\n\t\t\t\t\t\t\t\t\t\t\t  \"background file '%s' and no valid fallback provided\", src);\n\t\t\t\tfree(src);\n\t\t\t\treturn res;\n\t\t\t}\n\t\t\tsway_log(SWAY_DEBUG, \"Cannot access file '%s', using fallback '%s'\", src, fallback);\n\t\t\toutput->background = fallback;\n\t\t\tif (!(output->background_option = strdup(\"solid_color\"))) goto cleanup;\n\t\t\toutput->background_fallback = NULL;\n\t\t} else {\n\t\t\toutput->background = src;\n\t\t\tif (!(output->background_option = strdup(mode))) goto cleanup;\n\t\t}\n\t}\n\tconfig->handler_context.leftovers.argc = argc;\n\tconfig->handler_context.leftovers.argv = argv;\n\treturn NULL;\n\ncleanup:\n\tfree(src);\n\tsway_log(SWAY_ERROR, \"Failed to allocate resources\");\n\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate resources\");\n}\n"
  },
  {
    "path": "sway/commands/output/color_profile.c",
    "content": "#include <fcntl.h>\n#include <strings.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <wlr/render/color.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"stringop.h\"\n\nstatic bool read_file_into_buf(const char *path, void **buf, size_t *size) {\n\t/* Why not use fopen/fread directly? glibc will succesfully open directories,\n\t * not just files, and supports seeking on them. Instead, we directly\n\t * work with file descriptors and use the more consistent open/fstat/read. */\n\tint fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC);\n\tif (fd == -1) {\n\t\treturn false;\n\t}\n\tchar *b = NULL;\n\tstruct stat info;\n\tif (fstat(fd, &info) == -1) {\n\t\tgoto fail;\n\t}\n\t// only regular files, to avoid issues with e.g. opening pipes\n\tif (!S_ISREG(info.st_mode)) {\n\t\tgoto fail;\n\t}\n\toff_t s = info.st_size;\n\tif (s <= 0) {\n\t\tgoto fail;\n\t}\n\tb = calloc(1, s);\n\tif (!b) {\n\t\tgoto fail;\n\t}\n\tsize_t nread = 0;\n\twhile (nread < (size_t)s) {\n\t\tsize_t to_read = (size_t)s - nread;\n\t\tssize_t r = read(fd, b + nread, to_read);\n\t\tif ((r == -1 && errno != EINTR) || r == 0) {\n\t\t\tgoto fail;\n\t\t}\n\t\tnread += (size_t)r;\n\t}\n\tclose(fd);\n\t*buf = b;\n\t*size = (size_t)s;\n\treturn true; // success\nfail:\n\tfree(b);\n\tclose(fd);\n\treturn false;\n}\n\nstruct cmd_results *output_cmd_color_profile(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\n\tenum color_profile new_mode = COLOR_PROFILE_TRANSFORM;\n\tif (argc >= 2 && strcmp(*argv, \"--device-primaries\") == 0) {\n\t\tnew_mode = COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES;\n\t\targc--;\n\t\targv++;\n\t}\n\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing color_profile first argument.\");\n\t}\n\n\tif (strcmp(*argv, \"gamma22\") == 0) {\n\t\twlr_color_transform_unref(config->handler_context.output_config->color_transform);\n\t\tconfig->handler_context.output_config->color_transform = NULL;\n\t\tconfig->handler_context.output_config->color_profile = new_mode;\n\n\t\tconfig->handler_context.leftovers.argc = argc - 1;\n\t\tconfig->handler_context.leftovers.argv = argv + 1;\n\t} else if (strcmp(*argv, \"srgb\") == 0) {\n\t\twlr_color_transform_unref(config->handler_context.output_config->color_transform);\n\t\tconfig->handler_context.output_config->color_transform =\n\t\t\twlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_SRGB);\n\t\tconfig->handler_context.output_config->color_profile = new_mode;\n\n\t\tconfig->handler_context.leftovers.argc = argc - 1;\n\t\tconfig->handler_context.leftovers.argv = argv + 1;\n\t} else if (strcmp(*argv, \"icc\") == 0) {\n\t\tif (argc < 2) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Invalid color profile specification: icc type requires a file\");\n\t\t}\n\t\tif (new_mode != COLOR_PROFILE_TRANSFORM) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Invalid color profile specification: --device-primaries cannot be used with icc\");\n\t\t}\n\n\t\tchar *icc_path = strdup(argv[1]);\n\t\tif (!expand_path(&icc_path)) {\n\t\t\tstruct cmd_results *cmd_res = cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Invalid color profile specification: invalid file path\");\n\t\t\tfree(icc_path);\n\t\t\treturn cmd_res;\n\t\t}\n\n\t\tvoid *data = NULL;\n\t\tsize_t size = 0;\n\t\tif (!read_file_into_buf(icc_path, &data, &size)) {\n\t\t\tfree(icc_path);\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Failed to load color profile: could not read ICC file\");\n\t\t}\n\t\tfree(icc_path);\n\n\t\tstruct wlr_color_transform *tmp =\n\t\t\twlr_color_transform_init_linear_to_icc(data, size);\n\t\tif (!tmp) {\n\t\t\tfree(data);\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Failed to load color profile: failed to initialize transform from ICC\");\n\t\t}\n\t\tfree(data);\n\n\t\twlr_color_transform_unref(config->handler_context.output_config->color_transform);\n\t\tconfig->handler_context.output_config->color_transform = tmp;\n\t\tconfig->handler_context.output_config->color_profile = COLOR_PROFILE_TRANSFORM;\n\n\t\tconfig->handler_context.leftovers.argc = argc - 2;\n\t\tconfig->handler_context.leftovers.argv = argv + 2;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Invalid color profile specification: \"\n\t\t\t\"first argument should be gamma22|icc|srgb\");\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/disable.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *output_cmd_disable(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tconfig->handler_context.output_config->enabled = 0;\n\n\tconfig->handler_context.leftovers.argc = argc;\n\tconfig->handler_context.leftovers.argv = argv;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/dpms.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n\nstruct cmd_results *output_cmd_dpms(int argc, char **argv) {\n\tsway_log(SWAY_INFO, \"The \\\"output dpms\\\" command is deprecated, \"\n\t\t\"use \\\"output power\\\" instead\");\n\treturn output_cmd_power(argc, argv);\n}\n"
  },
  {
    "path": "sway/commands/output/enable.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *output_cmd_enable(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\n\tconfig->handler_context.output_config->enabled = 1;\n\n\tconfig->handler_context.leftovers.argc = argc;\n\tconfig->handler_context.leftovers.argv = argv;\n\treturn NULL;\n}\n\n"
  },
  {
    "path": "sway/commands/output/hdr.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"util.h\"\n\nstruct cmd_results *output_cmd_hdr(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (argc == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing hdr argument\");\n\t}\n\n\tbool current = false;\n\tif (strcasecmp(argv[0], \"toggle\") == 0) {\n\t\tconst char *oc_name = config->handler_context.output_config->name;\n\t\tif (strcmp(oc_name, \"*\") == 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Cannot apply toggle to all outputs\");\n\t\t}\n\n\t\tstruct sway_output *output = all_output_by_name_or_id(oc_name);\n\t\tif (!output) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Cannot apply toggle to unknown output %s\", oc_name);\n\t\t}\n\n\t\tcurrent = output->hdr;\n\t}\n\n\tconfig->handler_context.output_config->hdr = parse_boolean(argv[0], current);\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/max_render_time.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *output_cmd_max_render_time(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing max render time argument.\");\n\t}\n\n\tint max_render_time;\n\tif (!strcmp(*argv, \"off\")) {\n\t\tmax_render_time = 0;\n\t} else {\n\t\tchar *end;\n\t\tmax_render_time = strtol(*argv, &end, 10);\n\t\tif (*end || max_render_time <= 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid max render time.\");\n\t\t}\n\t}\n\tconfig->handler_context.output_config->max_render_time = max_render_time;\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/mode.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *output_cmd_mode(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing mode argument.\");\n\t}\n\n\tstruct output_config *output = config->handler_context.output_config;\n\n\tif (strcmp(argv[0], \"--custom\") == 0) {\n\t\targv++;\n\t\targc--;\n\t\toutput->custom_mode = 1;\n\t} else {\n\t\toutput->custom_mode = 0;\n\t}\n\n\t// Reset custom modeline, if any\n\toutput->drm_mode.type = 0;\n\n\tchar *end;\n\toutput->width = strtol(*argv, &end, 10);\n\tif (*end) {\n\t\t// Format is 1234x4321\n\t\tif (*end != 'x') {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid mode width.\");\n\t\t}\n\t\t++end;\n\t\toutput->height = strtol(end, &end, 10);\n\t\tif (*end) {\n\t\t\tif (*end != '@') {\n\t\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid mode height.\");\n\t\t\t}\n\t\t\t++end;\n\t\t\toutput->refresh_rate = strtof(end, &end);\n\t\t\tif (strcasecmp(\"Hz\", end) != 0) {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Invalid mode refresh rate.\");\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Format is 1234 4321\n\t\targc--; argv++;\n\t\tif (!argc) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Missing mode argument (height).\");\n\t\t}\n\t\toutput->height = strtol(*argv, &end, 10);\n\t\tif (*end) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid mode height.\");\n\t\t}\n\t}\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n\nstatic bool parse_modeline(char **argv, drmModeModeInfo *mode) {\n\tmode->type = DRM_MODE_TYPE_USERDEF;\n\tmode->clock = strtof(argv[0], NULL) * 1000;\n\tmode->hdisplay = strtol(argv[1], NULL, 10);\n\tmode->hsync_start = strtol(argv[2], NULL, 10);\n\tmode->hsync_end = strtol(argv[3], NULL, 10);\n\tmode->htotal = strtol(argv[4], NULL, 10);\n\tmode->vdisplay = strtol(argv[5], NULL, 10);\n\tmode->vsync_start = strtol(argv[6], NULL, 10);\n\tmode->vsync_end = strtol(argv[7], NULL, 10);\n\tmode->vtotal = strtol(argv[8], NULL, 10);\n\n\tmode->vrefresh = mode->clock * 1000.0 * 1000.0\n\t\t/ mode->htotal / mode->vtotal;\n\tif (strcasecmp(argv[9], \"+hsync\") == 0) {\n\t\tmode->flags |= DRM_MODE_FLAG_PHSYNC;\n\t} else if (strcasecmp(argv[9], \"-hsync\") == 0) {\n\t\tmode->flags |= DRM_MODE_FLAG_NHSYNC;\n\t} else {\n\t\treturn false;\n\t}\n\n\tif (strcasecmp(argv[10], \"+vsync\") == 0) {\n\t\tmode->flags |= DRM_MODE_FLAG_PVSYNC;\n\t} else if (strcasecmp(argv[10], \"-vsync\") == 0) {\n\t\tmode->flags |= DRM_MODE_FLAG_NVSYNC;\n\t} else {\n\t\treturn false;\n\t}\n\n\tsnprintf(mode->name, sizeof(mode->name), \"%dx%d@%d\",\n\t\t mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000);\n\n\treturn true;\n}\n\nstruct cmd_results *output_cmd_modeline(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing modeline argument.\");\n\t}\n\n\tstruct output_config *output = config->handler_context.output_config;\n\n\tif (argc != 11 || !parse_modeline(argv, &output->drm_mode)) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid modeline\");\n\t}\n\n\tconfig->handler_context.leftovers.argc = argc - 12;\n\tconfig->handler_context.leftovers.argv = argv + 12;\n\treturn NULL;\n}\n\n"
  },
  {
    "path": "sway/commands/output/position.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *output_cmd_position(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing position argument.\");\n\t}\n\n\tchar *end;\n\tconfig->handler_context.output_config->x = strtol(*argv, &end, 10);\n\tif (*end) {\n\t\t// Format is 1234,4321\n\t\tif (*end != ',') {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid position x.\");\n\t\t}\n\t\t++end;\n\t\tconfig->handler_context.output_config->y = strtol(end, &end, 10);\n\t\tif (*end) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid position y.\");\n\t\t}\n\t} else {\n\t\t// Format is 1234 4321 (legacy)\n\t\targc--; argv++;\n\t\tif (!argc) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Missing position argument (y).\");\n\t\t}\n\t\tconfig->handler_context.output_config->y = strtol(*argv, &end, 10);\n\t\tif (*end) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Invalid position y.\");\n\t\t}\n\t}\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n\n"
  },
  {
    "path": "sway/commands/output/power.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"util.h\"\n\nstruct cmd_results *output_cmd_power(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (argc == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing power argument\");\n\t}\n\n\tbool current = true;\n\tif (strcasecmp(argv[0], \"toggle\") == 0) {\n\t\tconst char *oc_name = config->handler_context.output_config->name;\n\t\tif (strcmp(oc_name, \"*\") == 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Cannot apply toggle to all outputs\");\n\t\t}\n\n\t\tstruct sway_output *sway_output = all_output_by_name_or_id(oc_name);\n\t\tif (!sway_output || !sway_output->wlr_output) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Cannot apply toggle to unknown output %s\", oc_name);\n\t\t}\n\n\t\tif (sway_output->enabled && !sway_output->wlr_output->enabled) {\n\t\t\tcurrent = false;\n\t\t}\n\t}\n\n\tif (parse_boolean(argv[0], current)) {\n\t\tconfig->handler_context.output_config->power = 1;\n\t} else {\n\t\tconfig->handler_context.output_config->power = 0;\n\t}\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/render_bit_depth.c",
    "content": "#include <drm_fourcc.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing bit depth argument.\");\n\t}\n\n\tif (strcmp(*argv, \"6\") == 0) {\n\t\tconfig->handler_context.output_config->render_bit_depth =\n\t\t\tRENDER_BIT_DEPTH_6;\n\t} else if (strcmp(*argv, \"8\") == 0) {\n\t\tconfig->handler_context.output_config->render_bit_depth =\n\t\t\tRENDER_BIT_DEPTH_8;\n\t} else if (strcmp(*argv, \"10\") == 0) {\n\t\tconfig->handler_context.output_config->render_bit_depth =\n\t\t\tRENDER_BIT_DEPTH_10;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Invalid bit depth. Must be a value in (6|8|10).\");\n\t}\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n\n"
  },
  {
    "path": "sway/commands/output/scale.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *output_cmd_scale(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing scale argument.\");\n\t}\n\n\tchar *end;\n\tconfig->handler_context.output_config->scale = strtof(*argv, &end);\n\tif (*end) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid scale.\");\n\t}\n\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/scale_filter.c",
    "content": "#include <string.h>\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n\nstruct cmd_results *output_cmd_scale_filter(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing scale_filter argument.\");\n\t}\n\n\n\tenum scale_filter_mode scale_filter;\n\tif (strcmp(*argv, \"linear\") == 0) {\n\t\tscale_filter = SCALE_FILTER_LINEAR;\n\t} else if (strcmp(*argv, \"nearest\") == 0) {\n\t\tscale_filter = SCALE_FILTER_NEAREST;\n\t} else if (strcmp(*argv, \"smart\") == 0) {\n\t\tscale_filter = SCALE_FILTER_SMART;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid output scale_filter.\");\n\t}\n\n\tstruct output_config *oc = config->handler_context.output_config;\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\n\toc->scale_filter = scale_filter;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/subpixel.c",
    "content": "#include <string.h>\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n\nstruct cmd_results *output_cmd_subpixel(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing subpixel argument.\");\n\t}\n\tenum wl_output_subpixel subpixel;\n\n\tif (strcmp(*argv, \"rgb\") == 0) {\n\t\tsubpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;\n\t} else if (strcmp(*argv, \"bgr\") == 0) {\n\t\tsubpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;\n\t} else if (strcmp(*argv, \"vrgb\") == 0) {\n\t\tsubpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;\n\t} else if (strcmp(*argv, \"vbgr\") == 0) {\n\t\tsubpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;\n\t} else if (strcmp(*argv, \"none\") == 0) {\n\t\tsubpixel = WL_OUTPUT_SUBPIXEL_NONE;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid output subpixel.\");\n\t}\n\n\tstruct output_config *oc = config->handler_context.output_config;\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\n\toc->subpixel = subpixel;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/toggle.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n\nstruct cmd_results *output_cmd_toggle(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\n\tstruct output_config *oc = config->handler_context.output_config;\n\n\tif (strcmp(oc->name, \"*\") == 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Cannot apply toggle to all outputs.\");\n\t}\n\n\tstruct sway_output *sway_output = all_output_by_name_or_id(oc->name);\n\n\tif (sway_output == NULL) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Cannot apply toggle to unknown output %s\", oc->name);\n\t}\n\n\toc = find_output_config(sway_output);\n\n\tif (!oc || oc->enabled != 0) {\n\t\tconfig->handler_context.output_config->enabled = 0;\n\t} else {\n\t\tconfig->handler_context.output_config->enabled = 1;\n\t}\n\n\tfree_output_config(oc);\n\tconfig->handler_context.leftovers.argc = argc;\n\tconfig->handler_context.leftovers.argv = argv;\n\treturn NULL;\n}\n\n"
  },
  {
    "path": "sway/commands/output/transform.c",
    "content": "#include <string.h>\n#include <wlr/util/transform.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n#include \"sway/output.h\"\n\nstatic enum wl_output_transform invert_rotation_direction(\n\t\tenum wl_output_transform t) {\n\tswitch (t) {\n\tcase WL_OUTPUT_TRANSFORM_90:\n\t\treturn WL_OUTPUT_TRANSFORM_270;\n\tcase WL_OUTPUT_TRANSFORM_270:\n\t\treturn WL_OUTPUT_TRANSFORM_90;\n\tcase WL_OUTPUT_TRANSFORM_FLIPPED_90:\n\t\treturn WL_OUTPUT_TRANSFORM_FLIPPED_270;\n\tcase WL_OUTPUT_TRANSFORM_FLIPPED_270:\n\t\treturn WL_OUTPUT_TRANSFORM_FLIPPED_90;\n\tdefault:\n\t\treturn t;\n\t}\n}\n\nstruct cmd_results *output_cmd_transform(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\tif (!argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Missing transform argument.\");\n\t}\n\n\tenum wl_output_transform transform;\n\tif (strcmp(*argv, \"normal\") == 0 ||\n\t\t\tstrcmp(*argv, \"0\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_NORMAL;\n\t} else if (strcmp(*argv, \"90\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_90;\n\t} else if (strcmp(*argv, \"180\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_180;\n\t} else if (strcmp(*argv, \"270\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_270;\n\t} else if (strcmp(*argv, \"flipped\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_FLIPPED;\n\t} else if (strcmp(*argv, \"flipped-90\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_FLIPPED_90;\n\t} else if (strcmp(*argv, \"flipped-180\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_FLIPPED_180;\n\t} else if (strcmp(*argv, \"flipped-270\") == 0) {\n\t\ttransform = WL_OUTPUT_TRANSFORM_FLIPPED_270;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid output transform.\");\n\t}\n\n\t// Sway uses clockwise transforms, while WL_OUTPUT_TRANSFORM_* describe\n\t// anti-clockwise transforms\n\ttransform = invert_rotation_direction(transform);\n\n\tstruct output_config *output = config->handler_context.output_config;\n\tconfig->handler_context.leftovers.argc = argc - 1;\n\tconfig->handler_context.leftovers.argv = argv + 1;\n\tif (argc > 1 &&\n\t\t\t(strcmp(argv[1], \"clockwise\") == 0 || strcmp(argv[1], \"anticlockwise\") == 0)) {\n\t\tif (config->reloading) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Relative transforms cannot be used in the configuration file\");\n\t\t}\n\t\tif (!sway_assert(output->name != NULL, \"Output config name not set\")) {\n\t\t\treturn NULL;\n\t\t}\n\t\tif (strcmp(output->name, \"*\") == 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Cannot apply relative transform to all outputs.\");\n\t\t}\n\t\tstruct sway_output *s_output = output_by_name_or_id(output->name);\n\t\tif (s_output == NULL) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Cannot apply relative transform to unknown output %s\", output->name);\n\t\t}\n\t\tif (strcmp(argv[1], \"anticlockwise\") == 0) {\n\t\t\ttransform = invert_rotation_direction(transform);\n\t\t}\n\t\tstruct wlr_output *w_output = s_output->wlr_output;\n\t\ttransform = wlr_output_transform_compose(w_output->transform, transform);\n\t\tconfig->handler_context.leftovers.argv += 1;\n\t\tconfig->handler_context.leftovers.argc -= 1;\n\t}\n\n\toutput->transform = transform;\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/commands/output/unplug.c",
    "content": "#include <strings.h>\n#include <wlr/config.h>\n#include <wlr/backend/headless.h>\n#include <wlr/backend/wayland.h>\n#if WLR_HAS_X11_BACKEND\n#include <wlr/backend/x11.h>\n#endif\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n\nstatic bool is_backend_allowed(struct wlr_backend *backend) {\n\tif (wlr_backend_is_headless(backend)) {\n\t\treturn true;\n\t}\n\tif (wlr_backend_is_wl(backend)) {\n\t\treturn true;\n\t}\n#if WLR_HAS_X11_BACKEND\n\tif (wlr_backend_is_x11(backend)) {\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\n/**\n * This command is intended for developer use only.\n */\nstruct cmd_results *output_cmd_unplug(int argc, char **argv) {\n\tif (!config->handler_context.output_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Missing output config\");\n\t}\n\n\tconst char *oc_name = config->handler_context.output_config->name;\n\tif (strcmp(oc_name, \"*\") == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Won't unplug all outputs\");\n\t}\n\n\tstruct sway_output *sway_output = all_output_by_name_or_id(oc_name);\n\tif (!sway_output) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Cannot unplug unknown output %s\", oc_name);\n\t}\n\n\tif (!is_backend_allowed(sway_output->wlr_output->backend)) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Can only unplug outputs with headless, wayland or x11 backend\");\n\t}\n\n\twlr_output_destroy(sway_output->wlr_output);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/output.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"list.h\"\n#include \"log.h\"\n\n// must be in order for the bsearch\nstatic const struct cmd_handler output_handlers[] = {\n\t{ \"adaptive_sync\", output_cmd_adaptive_sync },\n\t{ \"allow_tearing\", output_cmd_allow_tearing },\n\t{ \"background\", output_cmd_background },\n\t{ \"bg\", output_cmd_background },\n\t{ \"color_profile\", output_cmd_color_profile },\n\t{ \"disable\", output_cmd_disable },\n\t{ \"dpms\", output_cmd_dpms },\n\t{ \"enable\", output_cmd_enable },\n\t{ \"hdr\", output_cmd_hdr },\n\t{ \"max_render_time\", output_cmd_max_render_time },\n\t{ \"mode\", output_cmd_mode },\n\t{ \"modeline\", output_cmd_modeline },\n\t{ \"pos\", output_cmd_position },\n\t{ \"position\", output_cmd_position },\n\t{ \"power\", output_cmd_power },\n\t{ \"render_bit_depth\", output_cmd_render_bit_depth },\n\t{ \"res\", output_cmd_mode },\n\t{ \"resolution\", output_cmd_mode },\n\t{ \"scale\", output_cmd_scale },\n\t{ \"scale_filter\", output_cmd_scale_filter },\n\t{ \"subpixel\", output_cmd_subpixel },\n\t{ \"toggle\", output_cmd_toggle },\n\t{ \"transform\", output_cmd_transform },\n\t{ \"unplug\", output_cmd_unplug },\n};\n\nstruct cmd_results *cmd_output(int argc, char **argv) {\n\tstruct cmd_results *error = checkarg(argc, \"output\", EXPECTED_AT_LEAST, 1);\n\tif (error != NULL) {\n\t\treturn error;\n\t}\n\n\t// The HEADLESS-1 output is a dummy output used when there's no outputs\n\t// connected. It should never be configured.\n\tif (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Refusing to configure the no op output\");\n\t}\n\n\tstruct output_config *output = NULL;\n\tif (strcmp(argv[0], \"-\") == 0 || strcmp(argv[0], \"--\") == 0) {\n\t\tif (config->reading) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Current output alias (%s) cannot be used in the config\",\n\t\t\t\t\targv[0]);\n\t\t}\n\t\tstruct sway_output *sway_output = config->handler_context.node ?\n\t\t\tnode_get_output(config->handler_context.node) : NULL;\n\t\tif (!sway_output) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Unknown output\");\n\t\t}\n\t\tif (sway_output == root->fallback_output) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Refusing to configure the no op output\");\n\t\t}\n\t\tif (strcmp(argv[0], \"-\") == 0) {\n\t\t\toutput = new_output_config(sway_output->wlr_output->name);\n\t\t} else {\n\t\t\tchar identifier[128];\n\t\t\toutput_get_identifier(identifier, 128, sway_output);\n\t\t\toutput = new_output_config(identifier);\n\t\t}\n\t} else {\n\t\toutput = new_output_config(argv[0]);\n\t}\n\tif (!output) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate output config\");\n\t\treturn NULL;\n\t}\n\targc--; argv++;\n\n\tconfig->handler_context.output_config = output;\n\n\twhile (argc > 0) {\n\t\tconfig->handler_context.leftovers.argc = 0;\n\t\tconfig->handler_context.leftovers.argv = NULL;\n\n\t\tif (find_handler(*argv, output_handlers, sizeof(output_handlers))) {\n\t\t\terror = config_subcommand(argv, argc, output_handlers,\n\t\t\t\t\tsizeof(output_handlers));\n\t\t} else {\n\t\t\terror = cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Invalid output subcommand: %s.\", *argv);\n\t\t}\n\n\t\tif (error != NULL) {\n\t\t\tgoto fail;\n\t\t}\n\n\t\targc = config->handler_context.leftovers.argc;\n\t\targv = config->handler_context.leftovers.argv;\n\t}\n\n\tconfig->handler_context.output_config = NULL;\n\tconfig->handler_context.leftovers.argc = 0;\n\tconfig->handler_context.leftovers.argv = NULL;\n\n\tbool background = output->background;\n\n\tstore_output_config(output);\n\n\tif (config->reading) {\n\t\t// When reading the config file, we wait till the end to do a single\n\t\t// modeset and swaybg spawn.\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t}\n\trequest_modeset();\n\n\tif (background && !spawn_swaybg()) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\"Failed to apply background configuration\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\nfail:\n\tconfig->handler_context.output_config = NULL;\n\tfree_output_config(output);\n\treturn error;\n}\n"
  },
  {
    "path": "sway/commands/popup_during_fullscreen.c",
    "content": "#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *cmd_popup_during_fullscreen(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"popup_during_fullscreen\",\n\t\t\t\t\tEXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcasecmp(argv[0], \"smart\") == 0) {\n\t\tconfig->popup_during_fullscreen = POPUP_SMART;\n\t} else if (strcasecmp(argv[0], \"ignore\") == 0) {\n\t\tconfig->popup_during_fullscreen = POPUP_IGNORE;\n\t} else if (strcasecmp(argv[0], \"leave_fullscreen\") == 0) {\n\t\tconfig->popup_during_fullscreen = POPUP_LEAVE;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected \"\n\t\t\t\t\"'popup_during_fullscreen smart|ignore|leave_fullscreen'\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/primary_selection.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_primary_selection(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"primary_selection\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tbool primary_selection = parse_boolean(argv[0], true);\n\n\t// config->primary_selection is reset to the previous value on reload in\n\t// load_main_config()\n\tif (config->reloading && config->primary_selection != primary_selection) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"primary_selection can only be enabled/disabled at launch\");\n\t}\n\n\tconfig->primary_selection = primary_selection;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/reload.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nstatic void title_bar_update_iterator(struct sway_container *con, void *data) {\n\tcontainer_update_title_bar(con);\n}\n\nstatic void do_reload(void *data) {\n\t// store bar ids to check against new bars for barconfig_update events\n\tlist_t *bar_ids = create_list();\n\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\tstruct bar_config *bar = config->bars->items[i];\n\t\tlist_add(bar_ids, strdup(bar->id));\n\t}\n\n\tconst char *path = NULL;\n\tif (config->user_config_path) {\n\t\tpath = config->current_config_path;\n\t}\n\n\tif (!load_main_config(path, true, false)) {\n\t\tsway_log(SWAY_ERROR, \"Error(s) reloading config\");\n\t\tlist_free_items_and_destroy(bar_ids);\n\t\treturn;\n\t}\n\n\tipc_event_workspace(NULL, NULL, \"reload\");\n\n\tload_swaybars();\n\n\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\tstruct bar_config *bar = config->bars->items[i];\n\t\tfor (int j = 0; j < bar_ids->length; ++j) {\n\t\t\tif (strcmp(bar->id, bar_ids->items[j]) == 0) {\n\t\t\t\tipc_event_barconfig_update(bar);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tlist_free_items_and_destroy(bar_ids);\n\n\troot_for_each_container(title_bar_update_iterator, NULL);\n\n\tarrange_root();\n}\n\nstruct cmd_results *cmd_reload(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"reload\", EXPECTED_EQUAL_TO, 0))) {\n\t\treturn error;\n\t}\n\n\tconst char *path = NULL;\n\tif (config->user_config_path) {\n\t\tpath = config->current_config_path;\n\t}\n\n\tif (!load_main_config(path, true, true)) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Error(s) reloading config.\");\n\t}\n\n\t// The reload command frees a lot of stuff, so to avoid use-after-frees\n\t// we schedule the reload to happen using an idle event.\n\twl_event_loop_add_idle(server.wl_event_loop, do_reload, NULL);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/rename.c",
    "content": "#include <ctype.h>\n#include <string.h>\n#include <strings.h>\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/desktop/launcher.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/tree/root.h\"\n\nstatic const char expected_syntax[] =\n\t\"Expected 'rename workspace <old_name> to <new_name>' or \"\n\t\"'rename workspace to <new_name>'\";\n\nstruct cmd_results *cmd_rename(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"rename\", EXPECTED_AT_LEAST, 3))) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tif (strcasecmp(argv[0], \"workspace\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\n\tint argn = 1;\n\tstruct sway_workspace *workspace = NULL;\n\n\tif (strcasecmp(argv[1], \"to\") == 0) {\n\t\t// 'rename workspace to new_name'\n\t\tworkspace = config->handler_context.workspace;\n\t} else if (strcasecmp(argv[1], \"number\") == 0) {\n\t\t// 'rename workspace number x to new_name'\n\t\tif (!isdigit(argv[2][0])) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Invalid workspace number '%s'\", argv[2]);\n\t\t}\n\t\tworkspace = workspace_by_number(argv[2]);\n\t\twhile (argn < argc && strcasecmp(argv[argn], \"to\") != 0) {\n\t\t\t++argn;\n\t\t}\n\t} else {\n\t\t// 'rename workspace old_name to new_name'\n\t\tint end = argn;\n\t\twhile (end < argc && strcasecmp(argv[end], \"to\") != 0) {\n\t\t\t++end;\n\t\t}\n\t\tchar *old_name = join_args(argv + argn, end - argn);\n\t\tworkspace = workspace_by_name(old_name);\n\t\tfree(old_name);\n\t\targn = end;\n\t}\n\n\tif (!workspace) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"There is no workspace with that name\");\n\t}\n\n\t++argn; // move past \"to\"\n\n\tif (argn >= argc) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\n\tchar *new_name = join_args(argv + argn, argc - argn);\n\tif (strcasecmp(new_name, \"next\") == 0 ||\n\t\t\tstrcasecmp(new_name, \"prev\") == 0 ||\n\t\t\tstrcasecmp(new_name, \"next_on_output\") == 0 ||\n\t\t\tstrcasecmp(new_name, \"prev_on_output\") == 0 ||\n\t\t\tstrcasecmp(new_name, \"back_and_forth\") == 0 ||\n\t\t\tstrcasecmp(new_name, \"current\") == 0 ||\n\t\t\tstrcasecmp(new_name, \"number\") == 0) {\n\t\tfree(new_name);\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Cannot use special workspace name '%s'\", argv[argn]);\n\t}\n\tstruct sway_workspace *tmp_workspace = workspace_by_name(new_name);\n\tif (tmp_workspace) {\n\t\tfree(new_name);\n\t\tif (tmp_workspace == workspace) {\n\t\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t\t} else {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Workspace already exists\");\n\t\t}\n\t}\n\n\tsway_log(SWAY_DEBUG, \"renaming workspace '%s' to '%s'\", workspace->name, new_name);\n\n\tfree(workspace->name);\n\tworkspace->name = new_name;\n\n\toutput_sort_workspaces(workspace->output);\n\tipc_event_workspace(NULL, workspace, \"rename\");\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/resize.c",
    "content": "#include <errno.h>\n#include <limits.h>\n#include <math.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <wlr/util/edges.h>\n#include \"sway/commands.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"log.h\"\n#include \"util.h\"\n\n#define AXIS_HORIZONTAL (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)\n#define AXIS_VERTICAL   (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)\n\nstatic uint32_t parse_resize_axis(const char *axis) {\n\tif (strcasecmp(axis, \"width\") == 0 || strcasecmp(axis, \"horizontal\") == 0) {\n\t\treturn AXIS_HORIZONTAL;\n\t}\n\tif (strcasecmp(axis, \"height\") == 0 || strcasecmp(axis, \"vertical\") == 0) {\n\t\treturn AXIS_VERTICAL;\n\t}\n\tif (strcasecmp(axis, \"up\") == 0) {\n\t\treturn WLR_EDGE_TOP;\n\t}\n\tif (strcasecmp(axis, \"down\") == 0) {\n\t\treturn WLR_EDGE_BOTTOM;\n\t}\n\tif (strcasecmp(axis, \"left\") == 0) {\n\t\treturn WLR_EDGE_LEFT;\n\t}\n\tif (strcasecmp(axis, \"right\") == 0) {\n\t\treturn WLR_EDGE_RIGHT;\n\t}\n\treturn WLR_EDGE_NONE;\n}\n\nstatic bool is_horizontal(uint32_t axis) {\n\treturn axis & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT);\n}\n\nstruct sway_container *container_find_resize_parent(struct sway_container *con,\n\t\tuint32_t axis) {\n\tenum sway_container_layout parallel_layout =\n\t\tis_horizontal(axis) ? L_HORIZ : L_VERT;\n\tbool allow_first = axis != WLR_EDGE_TOP && axis != WLR_EDGE_LEFT;\n\tbool allow_last = axis != WLR_EDGE_RIGHT && axis != WLR_EDGE_BOTTOM;\n\n\twhile (con) {\n\t\tlist_t *siblings = container_get_siblings(con);\n\t\tint index = container_sibling_index(con);\n\t\tif (container_parent_layout(con) == parallel_layout &&\n\t\t\t\tsiblings->length > 1 && (allow_first || index > 0) &&\n\t\t\t\t(allow_last || index < siblings->length - 1)) {\n\t\t\treturn con;\n\t\t}\n\t\tcon = con->pending.parent;\n\t}\n\n\treturn NULL;\n}\n\nvoid container_resize_tiled(struct sway_container *con,\n\t\tuint32_t axis, int amount) {\n\tif (!con) {\n\t\treturn;\n\t}\n\n\tcon = container_find_resize_parent(con, axis);\n\tif (!con) {\n\t\t// Can't resize in this direction\n\t\treturn;\n\t}\n\n\tif (container_is_scratchpad_hidden_or_child(con)) {\n\t\treturn;\n\t}\n\n\t// For HORIZONTAL or VERTICAL, we are growing in two directions so select\n\t// all adjacent siblings. For RIGHT or DOWN or LEFT or UP select just the\n\t// previous or next sibling.\n\tlist_t *resize = create_list();\n\tlist_t *siblings = container_get_siblings(con);\n\tint index = container_sibling_index(con);\n\n\tif (axis == AXIS_HORIZONTAL || axis == AXIS_VERTICAL) {\n\t\tlist_cat(resize, siblings);\n\t} else if (axis == WLR_EDGE_TOP || axis == WLR_EDGE_LEFT) {\n\t\tif (!sway_assert(index > 0, \"Didn't expect first child\")) {\n\t\t\tgoto cleanup;\n\t\t}\n\t\tlist_add(resize, siblings->items[index - 1]);\n\t\tlist_add(resize, con);\n\t} else {\n\t\tif (!sway_assert(index < siblings->length - 1,\n\t\t\t\t\t\"Didn't expect last child\")) {\n\t\t\tgoto cleanup;\n\t\t}\n\t\tlist_add(resize, con);\n\t\tlist_add(resize, siblings->items[index + 1]);\n\t}\n\n\t// Apply new dimensions\n\tint sibling_amount = ceil((double)amount / (double)(resize->length - 1));\n\n\tif (is_horizontal(axis)) {\n\t\tfor (int i = 0; i < resize->length; i++) {\n\t\t\tstruct sway_container *sibling = resize->items[i];\n\t\t\tdouble change = sibling == con ? amount : -sibling_amount;\n\t\t\tif (sibling->pending.width + change < MIN_SANE_W) {\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t}\n\t\tif (con->child_total_width <= 0) {\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\t// We're going to resize so snap all the width fractions to full pixels\n\t\t// to avoid rounding issues\n\t\tfor (int i = 0; i < siblings->length; ++i) {\n\t\t\tstruct sway_container *con = siblings->items[i];\n\t\t\tcon->width_fraction = con->pending.width / con->child_total_width;\n\t\t}\n\n\t\tdouble amount_fraction = (double)amount / con->child_total_width;\n\t\tdouble sibling_amount_fraction =\n\t\t\tamount_fraction / (double)(resize->length - 1);\n\n\t\tfor (int i = 0; i < resize->length; i++) {\n\t\t\tstruct sway_container *sibling = resize->items[i];\n\t\t\tsibling->width_fraction +=\n\t\t\t\tsibling == con ? amount_fraction : -sibling_amount_fraction;\n\t\t}\n\t} else {\n\t\tfor (int i = 0; i < resize->length; i++) {\n\t\t\tstruct sway_container *sibling = resize->items[i];\n\t\t\tdouble change = sibling == con ? amount : -sibling_amount;\n\t\t\tif (sibling->pending.height + change < MIN_SANE_H) {\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t}\n\t\tif (con->child_total_height <= 0) {\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\t// We're going to resize so snap all the height fractions to full pixels\n\t\t// to avoid rounding issues\n\t\tfor (int i = 0; i < siblings->length; ++i) {\n\t\t\tstruct sway_container *con = siblings->items[i];\n\t\t\tcon->height_fraction = con->pending.height / con->child_total_height;\n\t\t}\n\n\t\tdouble amount_fraction = (double)amount / con->child_total_height;\n\t\tdouble sibling_amount_fraction =\n\t\t\tamount_fraction / (double)(resize->length - 1);\n\n\t\tfor (int i = 0; i < resize->length; i++) {\n\t\t\tstruct sway_container *sibling = resize->items[i];\n\t\t\tsibling->height_fraction +=\n\t\t\t\tsibling == con ? amount_fraction : -sibling_amount_fraction;\n\t\t}\n\t}\n\n\tif (con->pending.parent) {\n\t\tarrange_container(con->pending.parent);\n\t} else {\n\t\tarrange_workspace(con->pending.workspace);\n\t}\n\ncleanup:\n\tlist_free(resize);\n}\n\n/**\n * Implement `resize <grow|shrink>` for a floating container.\n */\nstatic struct cmd_results *resize_adjust_floating(uint32_t axis,\n\t\tstruct movement_amount *amount) {\n\tstruct sway_container *con = config->handler_context.container;\n\tint grow_width = 0, grow_height = 0;\n\n\tif (is_horizontal(axis)) {\n\t\tgrow_width = amount->amount;\n\t} else {\n\t\tgrow_height = amount->amount;\n\t}\n\n\t// Make sure we're not adjusting beyond floating min/max size\n\tint min_width, max_width, min_height, max_height;\n\tfloating_calculate_constraints(&min_width, &max_width,\n\t\t\t&min_height, &max_height);\n\tif (con->pending.width + grow_width < min_width) {\n\t\tgrow_width = min_width - con->pending.width;\n\t} else if (con->pending.width + grow_width > max_width) {\n\t\tgrow_width = max_width - con->pending.width;\n\t}\n\tif (con->pending.height + grow_height < min_height) {\n\t\tgrow_height = min_height - con->pending.height;\n\t} else if (con->pending.height + grow_height > max_height) {\n\t\tgrow_height = max_height - con->pending.height;\n\t}\n\tint grow_x = 0, grow_y = 0;\n\n\tif (axis == AXIS_HORIZONTAL) {\n\t\tgrow_x = -grow_width / 2;\n\t} else if (axis == AXIS_VERTICAL) {\n\t\tgrow_y = -grow_height / 2;\n\t} else if (axis == WLR_EDGE_TOP) {\n\t\tgrow_y = -grow_height;\n\t} else if (axis == WLR_EDGE_LEFT) {\n\t\tgrow_x = -grow_width;\n\t}\n\tif (grow_width == 0 && grow_height == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Cannot resize any further\");\n\t}\n\tcon->pending.x += grow_x;\n\tcon->pending.y += grow_y;\n\tcon->pending.width += grow_width;\n\tcon->pending.height += grow_height;\n\n\tcon->pending.content_x += grow_x;\n\tcon->pending.content_y += grow_y;\n\tcon->pending.content_width += grow_width;\n\tcon->pending.content_height += grow_height;\n\n\tarrange_container(con);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n/**\n * Implement `resize <grow|shrink>` for a tiled container.\n */\nstatic struct cmd_results *resize_adjust_tiled(uint32_t axis,\n\t\tstruct movement_amount *amount) {\n\tstruct sway_container *current = config->handler_context.container;\n\n\tif (container_is_scratchpad_hidden_or_child(current)) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Cannot resize a hidden scratchpad container\");\n\t}\n\n\tif (amount->unit == MOVEMENT_UNIT_DEFAULT) {\n\t\tamount->unit = MOVEMENT_UNIT_PPT;\n\t}\n\tif (amount->unit == MOVEMENT_UNIT_PPT) {\n        struct sway_container *parent = current->pending.parent;\n\t\tfloat pct = amount->amount / 100.0f;\n\n\t\tif (is_horizontal(axis)) {\n            while (parent && parent->pending.layout != L_HORIZ) {\n                parent = parent->pending.parent;\n            }\n            if (parent) {\n                amount->amount = (float)parent->pending.width * pct;\n            } else {\n                amount->amount = (float)current->pending.workspace->width * pct;\n            }\n\t\t} else {\n            while (parent && parent->pending.layout != L_VERT) {\n                parent = parent->pending.parent;\n            }\n            if (parent) {\n                amount->amount = (float)parent->pending.height * pct;\n            } else {\n                amount->amount = (float)current->pending.workspace->height * pct;\n            }\n\t\t}\n\t}\n\n\tdouble old_width = current->width_fraction;\n\tdouble old_height = current->height_fraction;\n\tcontainer_resize_tiled(current, axis, amount->amount);\n\tif (current->width_fraction == old_width &&\n\t\t\tcurrent->height_fraction == old_height) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Cannot resize any further\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n/**\n * Implement `resize set` for a tiled container.\n */\nstatic struct cmd_results *resize_set_tiled(struct sway_container *con,\n\t\tstruct movement_amount *width, struct movement_amount *height) {\n\n\tif (container_is_scratchpad_hidden_or_child(con)) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Cannot resize a hidden scratchpad container\");\n\t}\n\n\tif (width->amount) {\n\t\tif (width->unit == MOVEMENT_UNIT_PPT ||\n\t\t\t\twidth->unit == MOVEMENT_UNIT_DEFAULT) {\n\t\t\t// Convert to px\n\t\t\tstruct sway_container *parent = con->pending.parent;\n\t\t\twhile (parent && parent->pending.layout != L_HORIZ) {\n\t\t\t\tparent = parent->pending.parent;\n\t\t\t}\n\t\t\tif (parent) {\n\t\t\t\twidth->amount = parent->pending.width * width->amount / 100;\n\t\t\t} else {\n\t\t\t\twidth->amount = con->pending.workspace->width * width->amount / 100;\n\t\t\t}\n\t\t\twidth->unit = MOVEMENT_UNIT_PX;\n\t\t}\n\t\tif (width->unit == MOVEMENT_UNIT_PX) {\n\t\t\tcontainer_resize_tiled(con, AXIS_HORIZONTAL,\n\t\t\t\t\twidth->amount - con->pending.width);\n\t\t}\n\t}\n\n\tif (height->amount) {\n\t\tif (height->unit == MOVEMENT_UNIT_PPT ||\n\t\t\t\theight->unit == MOVEMENT_UNIT_DEFAULT) {\n\t\t\t// Convert to px\n\t\t\tstruct sway_container *parent = con->pending.parent;\n\t\t\twhile (parent && parent->pending.layout != L_VERT) {\n\t\t\t\tparent = parent->pending.parent;\n\t\t\t}\n\t\t\tif (parent) {\n\t\t\t\theight->amount = parent->pending.height * height->amount / 100;\n\t\t\t} else {\n\t\t\t\theight->amount = con->pending.workspace->height * height->amount / 100;\n\t\t\t}\n\t\t\theight->unit = MOVEMENT_UNIT_PX;\n\t\t}\n\t\tif (height->unit == MOVEMENT_UNIT_PX) {\n\t\t\tcontainer_resize_tiled(con, AXIS_VERTICAL,\n\t\t\t\t\theight->amount - con->pending.height);\n\t\t}\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n/**\n * Implement `resize set` for a floating container.\n */\nstatic struct cmd_results *resize_set_floating(struct sway_container *con,\n\t\tstruct movement_amount *width, struct movement_amount *height) {\n\tint min_width, max_width, min_height, max_height, grow_width = 0, grow_height = 0;\n\tfloating_calculate_constraints(&min_width, &max_width,\n\t\t\t&min_height, &max_height);\n\n\tif (width->amount) {\n\t\tswitch (width->unit) {\n\t\tcase MOVEMENT_UNIT_PPT:\n\t\t\tif (container_is_scratchpad_hidden(con)) {\n\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\"Cannot resize a hidden scratchpad container by ppt\");\n\t\t\t}\n\t\t\t// Convert to px\n\t\t\twidth->amount = con->pending.workspace->width * width->amount / 100;\n\t\t\twidth->unit = MOVEMENT_UNIT_PX;\n\t\t\t// Falls through\n\t\tcase MOVEMENT_UNIT_PX:\n\t\tcase MOVEMENT_UNIT_DEFAULT:\n\t\t\twidth->amount = fmax(min_width, fmin(width->amount, max_width));\n\t\t\tgrow_width = width->amount - con->pending.width;\n\t\t\tcon->pending.x -= grow_width / 2;\n\t\t\tcon->pending.width = width->amount;\n\t\t\tbreak;\n\t\tcase MOVEMENT_UNIT_INVALID:\n\t\t\tsway_assert(false, \"invalid width unit\");\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (height->amount) {\n\t\tswitch (height->unit) {\n\t\tcase MOVEMENT_UNIT_PPT:\n\t\t\tif (container_is_scratchpad_hidden(con)) {\n\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\"Cannot resize a hidden scratchpad container by ppt\");\n\t\t\t}\n\t\t\t// Convert to px\n\t\t\theight->amount = con->pending.workspace->height * height->amount / 100;\n\t\t\theight->unit = MOVEMENT_UNIT_PX;\n\t\t\t// Falls through\n\t\tcase MOVEMENT_UNIT_PX:\n\t\tcase MOVEMENT_UNIT_DEFAULT:\n\t\t\theight->amount = fmax(min_height, fmin(height->amount, max_height));\n\t\t\tgrow_height = height->amount - con->pending.height;\n\t\t\tcon->pending.y -= grow_height / 2;\n\t\t\tcon->pending.height = height->amount;\n\t\t\tbreak;\n\t\tcase MOVEMENT_UNIT_INVALID:\n\t\t\tsway_assert(false, \"invalid height unit\");\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tcon->pending.content_x -= grow_width / 2;\n\tcon->pending.content_y -= grow_height / 2;\n\tcon->pending.content_width += grow_width;\n\tcon->pending.content_height += grow_height;\n\n\tarrange_container(con);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n/**\n * resize set <args>\n *\n * args: [width] <width> [px|ppt]\n *     : height <height> [px|ppt]\n *     : [width] <width> [px|ppt] [height] <height> [px|ppt]\n */\nstatic struct cmd_results *cmd_resize_set(int argc, char **argv) {\n\tstruct cmd_results *error;\n\tif ((error = checkarg(argc, \"resize\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tconst char usage[] = \"Expected 'resize set [width] <width> [px|ppt]' or \"\n\t\t\"'resize set height <height> [px|ppt]' or \"\n\t\t\"'resize set [width] <width> [px|ppt] [height] <height> [px|ppt]'\";\n\n\t// Width\n\tstruct movement_amount width = {0};\n\tif (argc >= 2 && !strcmp(argv[0], \"width\") && strcmp(argv[1], \"height\")) {\n\t\targc--; argv++;\n\t}\n\tif (strcmp(argv[0], \"height\")) {\n\t\tint num_consumed_args = parse_movement_amount(argc, argv, &width);\n\t\targc -= num_consumed_args;\n\t\targv += num_consumed_args;\n\t\tif (width.unit == MOVEMENT_UNIT_INVALID) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t\t}\n\t}\n\n\t// Height\n\tstruct movement_amount height = {0};\n\tif (argc) {\n\t\tif (argc >= 2 && !strcmp(argv[0], \"height\")) {\n\t\t\targc--; argv++;\n\t\t}\n\t\tint num_consumed_args = parse_movement_amount(argc, argv, &height);\n\t\tif (argc > num_consumed_args) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t\t}\n\t\tif (height.unit == MOVEMENT_UNIT_INVALID) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t\t}\n\t}\n\n\t// If 0, don't resize that dimension\n\tstruct sway_container *con = config->handler_context.container;\n\tif (width.amount <= 0) {\n\t\twidth.amount = con->pending.width;\n\t}\n\tif (height.amount <= 0) {\n\t\theight.amount = con->pending.height;\n\t}\n\n\tif (container_is_floating(con)) {\n\t\treturn resize_set_floating(con, &width, &height);\n\t}\n\treturn resize_set_tiled(con, &width, &height);\n}\n\n/**\n * resize <grow|shrink> <args>\n *\n * args: <direction>\n * args: <direction> <amount> <unit>\n * args: <direction> <amount> <unit> or <amount> <other_unit>\n */\nstatic struct cmd_results *cmd_resize_adjust(int argc, char **argv,\n\t\tint multiplier) {\n\tconst char usage[] = \"Expected 'resize grow|shrink <direction> \"\n\t\t\"[<amount> px|ppt [or <amount> px|ppt]]'\";\n\tuint32_t axis = parse_resize_axis(*argv);\n\tif (axis == WLR_EDGE_NONE) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t}\n\t--argc; ++argv;\n\n\t// First amount\n\tstruct movement_amount first_amount;\n\tif (argc) {\n\t\tint num_consumed_args = parse_movement_amount(argc, argv, &first_amount);\n\t\targc -= num_consumed_args;\n\t\targv += num_consumed_args;\n\t\tif (first_amount.unit == MOVEMENT_UNIT_INVALID) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t\t}\n\t} else {\n\t\tfirst_amount.amount = 10;\n\t\tfirst_amount.unit = MOVEMENT_UNIT_DEFAULT;\n\t}\n\n\t// \"or\"\n\tif (argc) {\n\t\tif (strcmp(*argv, \"or\") != 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t\t}\n\t\t--argc; ++argv;\n\t}\n\n\t// Second amount\n\tstruct movement_amount second_amount;\n\tif (argc) {\n\t\tint num_consumed_args = parse_movement_amount(argc, argv, &second_amount);\n\t\tif (argc > num_consumed_args) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t\t}\n\t\tif (second_amount.unit == MOVEMENT_UNIT_INVALID) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n\t\t}\n\t} else {\n\t\tsecond_amount.amount = 0;\n\t\tsecond_amount.unit = MOVEMENT_UNIT_INVALID;\n\t}\n\n\tfirst_amount.amount *= multiplier;\n\tsecond_amount.amount *= multiplier;\n\n\tstruct sway_container *con = config->handler_context.container;\n\tif (container_is_floating(con)) {\n\t\t// Floating containers can only resize in px. Choose an amount which\n\t\t// uses px, with fallback to an amount that specified no unit.\n\t\tif (first_amount.unit == MOVEMENT_UNIT_PX) {\n\t\t\treturn resize_adjust_floating(axis, &first_amount);\n\t\t} else if (second_amount.unit == MOVEMENT_UNIT_PX) {\n\t\t\treturn resize_adjust_floating(axis, &second_amount);\n\t\t} else if (first_amount.unit == MOVEMENT_UNIT_DEFAULT) {\n\t\t\treturn resize_adjust_floating(axis, &first_amount);\n\t\t} else if (second_amount.unit == MOVEMENT_UNIT_DEFAULT) {\n\t\t\treturn resize_adjust_floating(axis, &second_amount);\n\t\t} else {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Floating containers cannot use ppt measurements\");\n\t\t}\n\t}\n\n\t// For tiling, prefer ppt -> default -> px\n\tif (first_amount.unit == MOVEMENT_UNIT_PPT) {\n\t\treturn resize_adjust_tiled(axis, &first_amount);\n\t} else if (second_amount.unit == MOVEMENT_UNIT_PPT) {\n\t\treturn resize_adjust_tiled(axis, &second_amount);\n\t} else if (first_amount.unit == MOVEMENT_UNIT_DEFAULT) {\n\t\treturn resize_adjust_tiled(axis, &first_amount);\n\t} else if (second_amount.unit == MOVEMENT_UNIT_DEFAULT) {\n\t\treturn resize_adjust_tiled(axis, &second_amount);\n\t} else {\n\t\treturn resize_adjust_tiled(axis, &first_amount);\n\t}\n}\n\nstruct cmd_results *cmd_resize(int argc, char **argv) {\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tstruct sway_container *current = config->handler_context.container;\n\tif (!current) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Cannot resize nothing\");\n\t}\n\n\tstruct cmd_results *error;\n\tif ((error = checkarg(argc, \"resize\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tif (strcasecmp(argv[0], \"set\") == 0) {\n\t\treturn cmd_resize_set(argc - 1, &argv[1]);\n\t}\n\tif (strcasecmp(argv[0], \"grow\") == 0) {\n\t\treturn cmd_resize_adjust(argc - 1, &argv[1], 1);\n\t}\n\tif (strcasecmp(argv[0], \"shrink\") == 0) {\n\t\treturn cmd_resize_adjust(argc - 1, &argv[1], -1);\n\t}\n\n\tconst char usage[] = \"Expected 'resize <shrink|grow> \"\n\t\t\"<width|height|up|down|left|right> [<amount>] [px|ppt]'\";\n\n\treturn cmd_results_new(CMD_INVALID, \"%s\", usage);\n}\n"
  },
  {
    "path": "sway/commands/scratchpad.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/workspace.h\"\n\nstatic void scratchpad_toggle_auto(void) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_container *focus = seat_get_focused_container(seat);\n\tstruct sway_workspace *ws = seat_get_focused_workspace(seat);\n\tif (!ws) {\n\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\"No focused workspace to toggle scratchpad windows on\");\n\t\treturn;\n\t}\n\n\t// If the focus is in a floating split container,\n\t// operate on the split container instead of the child.\n\tif (focus && container_is_floating_or_child(focus)) {\n\t\twhile (focus->pending.parent) {\n\t\t\tfocus = focus->pending.parent;\n\t\t}\n\t}\n\n\t// Check if the currently focused window is a scratchpad window and should\n\t// be hidden again.\n\tif (focus && focus->scratchpad) {\n\t\tsway_log(SWAY_DEBUG, \"Focus is a scratchpad window - hiding %s\",\n\t\t\t\tfocus->title);\n\t\troot_scratchpad_hide(focus);\n\t\treturn;\n\t}\n\n\t// Check if there is an unfocused scratchpad window on the current workspace\n\t// and focus it.\n\tfor (int i = 0; i < ws->floating->length; ++i) {\n\t\tstruct sway_container *floater = ws->floating->items[i];\n\t\tif (floater->scratchpad && focus != floater) {\n\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\"Focusing other scratchpad window (%s) in this workspace\",\n\t\t\t\t\tfloater->title);\n\t\t\troot_scratchpad_show(floater);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Check if there is a visible scratchpad window on another workspace.\n\t// In this case we move it to the current workspace.\n\tfor (int i = 0; i < root->scratchpad->length; ++i) {\n\t\tstruct sway_container *con = root->scratchpad->items[i];\n\t\tif (con->pending.parent) {\n\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\"Moving a visible scratchpad window (%s) to this workspace\",\n\t\t\t\t\tcon->title);\n\t\t\troot_scratchpad_show(con);\n\t\t\tipc_event_window(con, \"move\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Take the container at the bottom of the scratchpad list\n\tif (!sway_assert(root->scratchpad->length, \"Scratchpad is empty\")) {\n\t\treturn;\n\t}\n\tstruct sway_container *con = root->scratchpad->items[0];\n\tsway_log(SWAY_DEBUG, \"Showing %s from list\", con->title);\n\troot_scratchpad_show(con);\n\tipc_event_window(con, \"move\");\n}\n\nstatic void scratchpad_toggle_container(struct sway_container *con) {\n\tif (!sway_assert(con->scratchpad, \"Container isn't in the scratchpad\")) {\n\t\treturn;\n\t}\n\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_workspace *ws = seat_get_focused_workspace(seat);\n\t// Check if it matches a currently visible scratchpad window and hide it.\n\tif (con->pending.workspace && ws == con->pending.workspace) {\n\t\troot_scratchpad_hide(con);\n\t\treturn;\n\t}\n\n\troot_scratchpad_show(con);\n\tipc_event_window(con, \"move\");\n}\n\nstruct cmd_results *cmd_scratchpad(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"scratchpad\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (strcmp(argv[0], \"show\") != 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Expected 'scratchpad show'\");\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tif (!root->scratchpad->length) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Scratchpad is empty\");\n\t}\n\n\tif (config->handler_context.node_overridden) {\n\t\tstruct sway_container *con = config->handler_context.container;\n\n\t\t// If the container is in a floating split container,\n\t\t// operate on the split container instead of the child.\n\t\tif (con && container_is_floating_or_child(con)) {\n\t\t\twhile (con->pending.parent) {\n\t\t\t\tcon = con->pending.parent;\n\t\t\t}\n\t\t}\n\n\t\t// If using criteria, this command is executed for every container which\n\t\t// matches the criteria. If this container isn't in the scratchpad,\n\t\t// we'll return an error. The same is true if the\n\t\t// overridden node is not a container.\n\t\tif (!con || !con->scratchpad) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Container is not in scratchpad.\");\n\t\t}\n\t\tscratchpad_toggle_container(con);\n\t} else {\n\t\tscratchpad_toggle_auto();\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/attach.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *seat_cmd_attach(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"attach\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\tif (!config->active) {\n\t\treturn cmd_results_new(CMD_DEFER, NULL);\n\t}\n\n\tstruct seat_attachment_config *attachment = seat_attachment_config_new();\n\tif (!attachment) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Failed to allocate seat attachment config\");\n\t}\n\tattachment->identifier = strdup(argv[0]);\n\tlist_add(config->handler_context.seat_config->attachments, attachment);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/cursor.c",
    "content": "#include <linux/input-event-codes.h>\n\n#include <strings.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_pointer.h>\n#include \"sway/commands.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/server.h\"\n\nstatic struct cmd_results *press_or_release(struct sway_cursor *cursor,\n\t\tchar *action, char *button_str);\n\nstatic const char expected_syntax[] = \"Expected 'cursor <move> <x> <y>' or \"\n\t\t\t\t\t\"'cursor <set> <x> <y>' or \"\n\t\t\t\t\t\"'cursor <press|release> <button[1-9]|event-name-or-code>'\";\n\nstatic struct cmd_results *handle_command(struct sway_cursor *cursor,\n\t\tint argc, char **argv) {\n\tif (strcasecmp(argv[0], \"move\") == 0) {\n\t\tif (argc < 3) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t\t}\n\t\tint delta_x = strtol(argv[1], NULL, 10);\n\t\tint delta_y = strtol(argv[2], NULL, 10);\n\t\twlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y);\n\t\tcursor_rebase(cursor);\n\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t} else if (strcasecmp(argv[0], \"set\") == 0) {\n\t\tif (argc < 3) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t\t}\n\t\t// map absolute coords (0..1,0..1) to root container coords\n\t\tfloat x = strtof(argv[1], NULL) / root->width;\n\t\tfloat y = strtof(argv[2], NULL) / root->height;\n\t\twlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);\n\t\tcursor_rebase(cursor);\n\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t} else {\n\t\tif (argc < 2) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t\t}\n\t\tstruct cmd_results *error = NULL;\n\t\tif ((error = press_or_release(cursor, argv[0], argv[1]))) {\n\t\t\treturn error;\n\t\t}\n\t}\n\n\tcursor_handle_activity_from_idle_source(cursor, IDLE_SOURCE_POINTER);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *seat_cmd_cursor(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"cursor\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\tstruct seat_config *sc = config->handler_context.seat_config;\n\tif (!sc) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tif (config->reading || !config->active) {\n\t\treturn cmd_results_new(CMD_DEFER, NULL);\n\t}\n\n\tif (strcmp(sc->name, \"*\") != 0) {\n\t\tstruct sway_seat *seat = input_manager_get_seat(sc->name, false);\n\t\tif (!seat) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Seat %s does not exist\", sc->name);\n\t\t}\n\t\terror = handle_command(seat->cursor, argc, argv);\n\t} else {\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\terror = handle_command(seat->cursor, argc, argv);\n\t\t\tif (error && error->status != CMD_SUCCESS) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn error ? error : cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *press_or_release(struct sway_cursor *cursor,\n\t\tchar *action, char *button_str) {\n\tenum wl_pointer_button_state state;\n\tuint32_t button;\n\tif (strcasecmp(action, \"press\") == 0) {\n\t\tstate = WL_POINTER_BUTTON_STATE_PRESSED;\n\t} else if (strcasecmp(action, \"release\") == 0) {\n\t\tstate = WL_POINTER_BUTTON_STATE_RELEASED;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\n\tchar *message = NULL;\n\tbutton = get_mouse_button(button_str, &message);\n\tif (message) {\n\t\tstruct cmd_results *error =\n\t\t\tcmd_results_new(CMD_INVALID, \"%s\", message);\n\t\tfree(message);\n\t\treturn error;\n\t} else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN\n\t\t\t|| button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {\n\t\t// Dispatch axis event\n\t\tenum wl_pointer_axis orientation =\n\t\t\t(button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)\n\t\t\t? WL_POINTER_AXIS_VERTICAL_SCROLL\n\t\t\t: WL_POINTER_AXIS_HORIZONTAL_SCROLL;\n\t\tdouble delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)\n\t\t\t? -1 : 1;\n\t\tstruct wlr_pointer_axis_event event = {\n\t\t\t.pointer = NULL,\n\t\t\t.time_msec = 0,\n\t\t\t.source = WL_POINTER_AXIS_SOURCE_WHEEL,\n\t\t\t.orientation = orientation,\n\t\t\t.delta = delta * 15,\n\t\t\t.delta_discrete = delta\n\t\t};\n\t\tdispatch_cursor_axis(cursor, &event);\n\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t\treturn cmd_results_new(CMD_SUCCESS, NULL);\n\t} else if (!button) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Unknown button %s\", button_str);\n\t}\n\tdispatch_cursor_button(cursor, NULL, 0, button, state);\n\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/fallback.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/commands.h\"\n#include \"util.h\"\n\nstruct cmd_results *seat_cmd_fallback(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"fallback\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tconfig->handler_context.seat_config->fallback =\n\t\tparse_boolean(argv[0], false);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/hide_cursor.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/server.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nstruct cmd_results *seat_cmd_hide_cursor(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"hide_cursor\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif ((error = checkarg(argc, \"hide_cursor\", EXPECTED_AT_MOST, 2))) {\n\t\treturn error;\n\t}\n\tstruct seat_config *seat_config = config->handler_context.seat_config;\n\tif (!seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tif (argc == 1) {\n\t\tchar *end;\n\t\tint timeout = strtol(argv[0], &end, 10);\n\t\tif (*end) {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Expected an integer timeout\");\n\t\t}\n\t\tif (timeout < 100 && timeout != 0) {\n\t\t\ttimeout = 100;\n\t\t}\n\t\tseat_config->hide_cursor_timeout = timeout;\n\t} else {\n\t\tif (strcmp(argv[0], \"when-typing\") != 0) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'hide_cursor <timeout>|when-typing [enable|disable]'\");\n\t\t}\n\t\tseat_config->hide_cursor_when_typing = parse_boolean(argv[1], true) ?\n\t\t\tHIDE_WHEN_TYPING_ENABLE : HIDE_WHEN_TYPING_DISABLE;\n\n\t\t// Invalidate all the caches for this config\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tseat->cursor->hide_when_typing = HIDE_WHEN_TYPING_DEFAULT;\n\t\t}\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/idle.c",
    "content": "#include <limits.h>\n#include <string.h>\n#include <strings.h>\n#include <stdint.h>\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/seat.h\"\n\nstatic const struct {\n\tconst char *name;\n\tuint32_t value;\n} idle_source_strings[] = {\n\t{ \"keyboard\", IDLE_SOURCE_KEYBOARD },\n\t{ \"pointer\", IDLE_SOURCE_POINTER },\n\t{ \"touch\", IDLE_SOURCE_TOUCH },\n\t{ \"tablet_pad\", IDLE_SOURCE_TABLET_PAD },\n\t{ \"tablet_tool\", IDLE_SOURCE_TABLET_TOOL },\n\t{ \"switch\", IDLE_SOURCE_SWITCH },\n};\n\nstatic uint32_t parse_sources(int argc, char **argv) {\n\tuint32_t sources = 0;\n\tfor (int i = 0; i < argc; ++i) {\n\t\tuint32_t value = 0;\n\t\tfor (size_t j = 0; j < sizeof(idle_source_strings)\n\t\t\t\t/ sizeof(idle_source_strings[0]); ++j) {\n\t\t\tif (strcasecmp(idle_source_strings[j].name, argv[i]) == 0) {\n\t\t\t\tvalue = idle_source_strings[j].value;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (value == 0) {\n\t\t\treturn UINT32_MAX;\n\t\t}\n\t\tsources |= value;\n\t}\n\treturn sources;\n}\n\nstruct cmd_results *seat_cmd_idle_inhibit(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"idle_inhibit\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tuint32_t sources = parse_sources(argc, argv);\n\tif (sources == UINT32_MAX) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid idle source\");\n\t}\n\tconfig->handler_context.seat_config->idle_inhibit_sources = sources;\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *seat_cmd_idle_wake(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"idle_wake\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tuint32_t sources = parse_sources(argc, argv);\n\tif (sources == UINT32_MAX) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid idle source\");\n\t}\n\tconfig->handler_context.seat_config->idle_wake_sources = sources;\n\tsway_log(SWAY_INFO, \"Warning: seat idle_wake is deprecated\");\n\tif (config->reading) {\n\t\tconfig_add_swaynag_warning(\"seat idle_wake is deprecated. \"\n\t\t\t\"Only seat idle_inhibit is supported.\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/keyboard_grouping.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *seat_cmd_keyboard_grouping(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"keyboard_grouping\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_INVALID, \"No seat defined\");\n\t}\n\n\tstruct seat_config *seat_config = config->handler_context.seat_config;\n\tif (strcmp(argv[0], \"none\") == 0) {\n\t\tseat_config->keyboard_grouping = KEYBOARD_GROUP_NONE;\n\t} else if (strcmp(argv[0], \"smart\") == 0) {\n\t\tseat_config->keyboard_grouping = KEYBOARD_GROUP_SMART;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected syntax `keyboard_grouping none|smart`\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/pointer_constraint.c",
    "content": "#include <string.h>\n#include <wlr/types/wlr_pointer_constraints_v1.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/server.h\"\n\nenum operation {\n\tOP_ENABLE,\n\tOP_DISABLE,\n\tOP_ESCAPE,\n};\n\n// pointer_constraint [enable|disable|escape]\nstruct cmd_results *seat_cmd_pointer_constraint(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"pointer_constraint\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tenum operation op;\n\tif (strcmp(argv[0], \"enable\") == 0) {\n\t\top = OP_ENABLE;\n\t} else if (strcmp(argv[0], \"disable\") == 0) {\n\t\top = OP_DISABLE;\n\t} else if (strcmp(argv[0], \"escape\") == 0) {\n\t\top = OP_ESCAPE;\n\t} else {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Expected enable|disable|escape\");\n\t}\n\n\tif (op == OP_ESCAPE && config->reading) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Can only escape at runtime.\");\n\t}\n\n\tstruct seat_config *seat_config = config->handler_context.seat_config;\n\tswitch (op) {\n\tcase OP_ENABLE:\n\t\tseat_config->allow_constrain = CONSTRAIN_ENABLE;\n\t\tbreak;\n\tcase OP_DISABLE:\n\t\tseat_config->allow_constrain = CONSTRAIN_DISABLE;\n\t\t/* fallthrough */\n\tcase OP_ESCAPE:;\n\t\tbool wildcard = !strcmp(seat_config->name, \"*\");\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tif (wildcard || !strcmp(seat->wlr_seat->name, seat_config->name)) {\n\t\t\t\tsway_cursor_constrain(seat->cursor, NULL);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/shortcuts_inhibitor.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/server.h\"\n#include \"util.h\"\n\nstatic struct cmd_results *handle_action(struct seat_config *sc,\n\t\tstruct sway_seat *seat, const char *action) {\n\tstruct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = NULL;\n\tif (strcmp(action, \"disable\") == 0) {\n\t\tsc->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE;\n\n\t\twl_list_for_each(sway_inhibitor,\n\t\t\t\t&seat->keyboard_shortcuts_inhibitors, link) {\n\t\t\twlr_keyboard_shortcuts_inhibitor_v1_deactivate(\n\t\t\t\t\tsway_inhibitor->inhibitor);\n\t\t}\n\n\t\tsway_log(SWAY_DEBUG, \"Deactivated all keyboard shortcuts inhibitors\");\n\t} else {\n\t\tsway_inhibitor = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);\n\t\tif (!sway_inhibitor) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"No inhibitor found for focused surface\");\n\t\t}\n\n\t\tstruct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor =\n\t\t\tsway_inhibitor->inhibitor;\n\t\tbool inhibit;\n\t\tif (strcmp(action, \"activate\") == 0) {\n\t\t\tinhibit = true;\n\t\t} else if (strcmp(action, \"deactivate\") == 0) {\n\t\t\tinhibit = false;\n\t\t} else if (strcmp(action, \"toggle\") == 0) {\n\t\t\tinhibit = !inhibitor->active;\n\t\t} else {\n\t\t\treturn cmd_results_new(CMD_INVALID, \"Expected enable|\"\n\t\t\t\t\t\"disable|activate|deactivate|toggle\");\n\t\t}\n\n\t\tif (inhibit) {\n\t\t\twlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor);\n\t\t} else {\n\t\t\twlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor);\n\t\t}\n\n\t\tsway_log(SWAY_DEBUG, \"%sctivated keyboard shortcuts inhibitor\",\n\t\t\t\tinhibit ? \"A\" : \"Dea\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\n// shortcuts_inhibitor [enable|disable|activate|deactivate|toggle]\nstruct cmd_results *seat_cmd_shortcuts_inhibitor(int argc, char **argv) {\n\tstruct cmd_results *error =\n\t\tcheckarg(argc, \"shortcuts_inhibitor\", EXPECTED_EQUAL_TO, 1);\n\tif (error) {\n\t\treturn error;\n\t}\n\n\tstruct seat_config *sc = config->handler_context.seat_config;\n\tif (!sc) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tif (strcmp(argv[0], \"enable\") == 0) {\n\t\tsc->shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;\n\t// at runtime disable is an action that also deactivates all active\n\t// inhibitors handled in handle_action()\n\t} else if (strcmp(argv[0], \"disable\") == 0 && !config->active) {\n\t\tsc->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE;\n\t} else if (!config->active) {\n\t\treturn cmd_results_new(CMD_INVALID, \"only enable and disable \"\n\t\t\t\t\"can be used in the config\");\n\t} else {\n\t\tif (strcmp(sc->name, \"*\") != 0) {\n\t\t\tstruct sway_seat *seat = input_manager_get_seat(sc->name, false);\n\t\t\tif (!seat) {\n\t\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\t\"Seat %s does not exist\", sc->name);\n\t\t\t}\n\t\t\terror = handle_action(sc, seat, argv[0]);\n\t\t} else {\n\t\t\tstruct sway_seat *seat = NULL;\n\t\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\t\terror = handle_action(sc, seat, argv[0]);\n\t\t\t\tif (error && error->status != CMD_SUCCESS) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn error ? error : cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat/xcursor_theme.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n\nstruct cmd_results *seat_cmd_xcursor_theme(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xcursor_theme\", EXPECTED_AT_LEAST, 1)) ||\n\t\t(error = checkarg(argc, \"xcursor_theme\", EXPECTED_AT_MOST, 2))) {\n\t\treturn error;\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No seat defined\");\n\t}\n\n\tconst char *theme_name = argv[0];\n\tunsigned size = 24;\n\n\tif (argc == 2) {\n\t\tchar *end;\n\t\tsize = strtoul(argv[1], &end, 10);\n\t\tif (*end) {\n\t\t\treturn cmd_results_new(\n\t\t\t\tCMD_INVALID, \"Expected a positive integer size\");\n\t\t}\n\t}\n\n\tfree(config->handler_context.seat_config->xcursor_theme.name);\n\tconfig->handler_context.seat_config->xcursor_theme.name = strdup(theme_name);\n\tconfig->handler_context.seat_config->xcursor_theme.size = size;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/seat.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\n// must be in order for the bsearch\n// these handlers perform actions on the seat\nstatic const struct cmd_handler seat_action_handlers[] = {\n\t{ \"cursor\", seat_cmd_cursor },\n};\n\n// must be in order for the bsearch\n// these handlers alter the seat config\nstatic const struct cmd_handler seat_handlers[] = {\n\t{ \"attach\", seat_cmd_attach },\n\t{ \"fallback\", seat_cmd_fallback },\n\t{ \"hide_cursor\", seat_cmd_hide_cursor },\n\t{ \"idle_inhibit\", seat_cmd_idle_inhibit },\n\t{ \"idle_wake\", seat_cmd_idle_wake },\n\t{ \"keyboard_grouping\", seat_cmd_keyboard_grouping },\n\t{ \"pointer_constraint\", seat_cmd_pointer_constraint },\n\t{ \"shortcuts_inhibitor\", seat_cmd_shortcuts_inhibitor },\n\t{ \"xcursor_theme\", seat_cmd_xcursor_theme },\n};\n\nstatic struct cmd_results *action_handlers(int argc, char **argv) {\n\tstruct cmd_results *res = config_subcommand(argv, argc,\n\t\t\tseat_action_handlers, sizeof(seat_action_handlers));\n\tfree_seat_config(config->handler_context.seat_config);\n\tconfig->handler_context.seat_config = NULL;\n\treturn res;\n}\n\nstatic struct cmd_results *config_handlers(int argc, char **argv) {\n\tstruct cmd_results *res = config_subcommand(argv, argc,\n\t\t\tseat_handlers, sizeof(seat_handlers));\n\tif (res && res->status != CMD_SUCCESS) {\n\t\tfree_seat_config(config->handler_context.seat_config);\n\t} else {\n\t\tstruct seat_config *sc =\n\t\t\tstore_seat_config(config->handler_context.seat_config);\n\t\tif (!config->reading) {\n\t\t\tinput_manager_apply_seat_config(sc);\n\t\t}\n\t}\n\tconfig->handler_context.seat_config = NULL;\n\treturn res;\n}\n\nstruct cmd_results *cmd_seat(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"seat\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tif (!strcmp(argv[0], \"-\")) {\n\t\tif (config->reading) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Current seat alias (-) cannot be used in the config\");\n\t\t}\n\t\tconfig->handler_context.seat_config =\n\t\t\tnew_seat_config(config->handler_context.seat->wlr_seat->name);\n\t} else {\n\t\tconfig->handler_context.seat_config = new_seat_config(argv[0]);\n\t}\n\tif (!config->handler_context.seat_config) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Couldn't allocate config\");\n\t}\n\n\tstruct cmd_results *res = NULL;\n\tif (find_handler(argv[1], seat_action_handlers,\n\t\t\t\tsizeof(seat_action_handlers))) {\n\t\tres = action_handlers(argc - 1, argv + 1);\n\t} else {\n\t\tres = config_handlers(argc - 1, argv + 1);\n\t}\n\treturn res ? res : cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/set.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\n// sort in order of longest->shortest\nstatic int compare_set_qsort(const void *_l, const void *_r) {\n\tstruct sway_variable const *l = *(void **)_l;\n\tstruct sway_variable const *r = *(void **)_r;\n\treturn strlen(r->name) - strlen(l->name);\n}\n\nvoid free_sway_variable(struct sway_variable *var) {\n\tif (!var) {\n\t\treturn;\n\t}\n\tfree(var->name);\n\tfree(var->value);\n\tfree(var);\n}\n\nstruct cmd_results *cmd_set(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"set\", EXPECTED_AT_LEAST, 2))) {\n\t\treturn error;\n\t}\n\n\tif (argv[0][0] != '$') {\n\t\treturn cmd_results_new(CMD_INVALID, \"variable '%s' must start with $\", argv[0]);\n\t}\n\n\tstruct sway_variable *var = NULL;\n\t// Find old variable if it exists\n\tint i;\n\tfor (i = 0; i < config->symbols->length; ++i) {\n\t\tvar = config->symbols->items[i];\n\t\tif (strcmp(var->name, argv[0]) == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tvar = NULL;\n\t}\n\tif (var) {\n\t\tfree(var->value);\n\t} else {\n\t\tvar = malloc(sizeof(struct sway_variable));\n\t\tif (!var) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Unable to allocate variable\");\n\t\t}\n\t\tvar->name = strdup(argv[0]);\n\t\tlist_add(config->symbols, var);\n\t\tlist_qsort(config->symbols, compare_set_qsort);\n\t}\n\tvar->value = join_args(argv + 1, argc - 1);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/shortcuts_inhibitor.c",
    "content": "#include <string.h>\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n\nstruct cmd_results *cmd_shortcuts_inhibitor(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"shortcuts_inhibitor\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_container *con = config->handler_context.container;\n\tif (!con || !con->view) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Only views can have shortcuts inhibitors\");\n\t}\n\n\tstruct sway_view *view = con->view;\n\tif (strcmp(argv[0], \"enable\") == 0) {\n\t\tview->shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;\n\t} else if (strcmp(argv[0], \"disable\") == 0) {\n\t\tview->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE;\n\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tstruct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =\n\t\t\t\tkeyboard_shortcuts_inhibitor_get_for_surface(\n\t\t\t\t\t\tseat, view->surface);\n\t\t\tif (!sway_inhibitor) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\twlr_keyboard_shortcuts_inhibitor_v1_deactivate(\n\t\t\t\t\tsway_inhibitor->inhibitor);\n\t\t\tsway_log(SWAY_DEBUG, \"Deactivated keyboard shortcuts \"\n\t\t\t\t\t\"inhibitor for seat %s on view\",\n\t\t\t\t\tseat->wlr_seat->name);\n\n\t\t}\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected `shortcuts_inhibitor enable|disable`\");\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/show_marks.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/output.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nstatic void title_bar_update_iterator(struct sway_container *con, void *data) {\n\tcontainer_update_marks(con);\n}\n\nstruct cmd_results *cmd_show_marks(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"show_marks\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tconfig->show_marks = parse_boolean(argv[0], config->show_marks);\n\n\tif (config->show_marks) {\n\t\troot_for_each_container(title_bar_update_iterator, NULL);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/smart_borders.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_smart_borders(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"smart_borders\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcmp(argv[0], \"no_gaps\") == 0) {\n\t\tconfig->hide_edge_borders_smart = ESMART_NO_GAPS;\n\t} else {\n\t\tconfig->hide_edge_borders_smart = parse_boolean(argv[0], true) ?\n\t\t\tESMART_ON : ESMART_OFF;\n\t}\n\n\tarrange_root();\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/smart_gaps.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/container.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_smart_gaps(int argc, char **argv) {\n\tstruct cmd_results *error = checkarg(argc, \"smart_gaps\", EXPECTED_AT_LEAST, 1);\n\n\tif (error) {\n\t\treturn error;\n\t}\n\n\tif (strcmp(argv[0], \"inverse_outer\") == 0) {\n\t\tconfig->smart_gaps = SMART_GAPS_INVERSE_OUTER;\n\t} else {\n\t\tconfig->smart_gaps = parse_boolean(argv[0], config->smart_gaps)\n\t\t\t? SMART_GAPS_ON : SMART_GAPS_OFF;\n\t}\n\n\tarrange_root();\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/split.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"log.h\"\n\nstatic struct cmd_results *do_split(int layout) {\n\tstruct sway_container *con = config->handler_context.container;\n\tstruct sway_workspace *ws = config->handler_context.workspace;\n\tif (con) {\n\t\tif (container_is_scratchpad_hidden_or_child(con) &&\n\t\t\t\tcon->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Cannot split a hidden scratchpad container\");\n\t\t}\n\t\tcontainer_split(con, layout);\n\t} else {\n\t\tworkspace_split(ws, layout);\n\t}\n\n\tif (root->fullscreen_global) {\n\t\tarrange_root();\n\t} else {\n\t\tarrange_workspace(ws);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstatic struct cmd_results *do_unsplit(void) {\n\tstruct sway_container *con = config->handler_context.container;\n\tstruct sway_workspace *ws = config->handler_context.workspace;\n\n\tif (con && con->pending.parent && con->pending.parent->pending.children->length == 1) {\n\t\tcontainer_flatten(con->pending.parent);\n\t} else {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Can only flatten a child container with no siblings\");\n\t}\n\n\tif (root->fullscreen_global) {\n\t\tarrange_root();\n\t} else {\n\t\tarrange_workspace(ws);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *cmd_split(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"split\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\tif (strcasecmp(argv[0], \"v\") == 0 || strcasecmp(argv[0], \"vertical\") == 0) {\n\t\treturn do_split(L_VERT);\n\t} else if (strcasecmp(argv[0], \"h\") == 0 ||\n\t\t\tstrcasecmp(argv[0], \"horizontal\") == 0) {\n\t\treturn do_split(L_HORIZ);\n\t} else if (strcasecmp(argv[0], \"t\") == 0 ||\n\t\t\tstrcasecmp(argv[0], \"toggle\") == 0) {\n\t\tstruct sway_container *focused = config->handler_context.container;\n\n\t\tif (focused && container_parent_layout(focused) == L_VERT) {\n\t\t\treturn do_split(L_HORIZ);\n\t\t} else {\n\t\t\treturn do_split(L_VERT);\n\t\t}\n\t} else if (strcasecmp(argv[0], \"n\") == 0 ||\n\t\t\tstrcasecmp(argv[0], \"none\") == 0) {\n\t\treturn do_unsplit();\n\t} else {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\"Invalid split command (expected either horizontal or vertical).\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n\nstruct cmd_results *cmd_splitv(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"splitv\", EXPECTED_EQUAL_TO, 0))) {\n\t\treturn error;\n\t}\n\treturn do_split(L_VERT);\n}\n\nstruct cmd_results *cmd_splith(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"splith\", EXPECTED_EQUAL_TO, 0))) {\n\t\treturn error;\n\t}\n\treturn do_split(L_HORIZ);\n}\n\nstruct cmd_results *cmd_splitt(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"splitt\", EXPECTED_EQUAL_TO, 0))) {\n\t\treturn error;\n\t}\n\n\tstruct sway_container *con = config->handler_context.container;\n\n\tif (con && container_parent_layout(con) == L_VERT) {\n\t\treturn do_split(L_HORIZ);\n\t} else {\n\t\treturn do_split(L_VERT);\n\t}\n}\n"
  },
  {
    "path": "sway/commands/sticky.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_sticky(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"sticky\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct sway_container *container = config->handler_context.container;\n\n\tif (container == NULL) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No current container\");\n\t};\n\n\tcontainer->is_sticky = parse_boolean(argv[0], container->is_sticky);\n\n\tif (container_is_sticky_or_child(container) &&\n\t\t\t!container_is_scratchpad_hidden(container)) {\n\t\t// move container to active workspace\n\t\tstruct sway_workspace *active_workspace =\n\t\t\toutput_get_active_workspace(container->pending.workspace->output);\n\t\tif (!sway_assert(active_workspace,\n\t\t\t\t\t\"Expected output to have a workspace\")) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Expected output to have a workspace\");\n\t\t}\n\t\tif (container->pending.workspace != active_workspace) {\n\t\t\tstruct sway_workspace *old_workspace = container->pending.workspace;\n\t\t\tcontainer_detach(container);\n\t\t\tworkspace_add_floating(active_workspace, container);\n\t\t\tcontainer_handle_fullscreen_reparent(container);\n\t\t\tarrange_workspace(active_workspace);\n\t\t\tworkspace_consider_destroy(old_workspace);\n\t\t}\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/swap.c",
    "content": "#include <strings.h>\n#include \"config.h\"\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"stringop.h\"\n\nstatic const char expected_syntax[] =\n\t\"Expected 'swap container with id|con_id|mark <arg>'\";\n\nstatic bool test_con_id(struct sway_container *container, void *data) {\n\tsize_t *con_id = data;\n\treturn container->node.id == *con_id;\n}\n\n#if WLR_HAS_XWAYLAND\nstatic bool test_id(struct sway_container *container, void *data) {\n\txcb_window_t *wid = data;\n\treturn (container->view && container->view->type == SWAY_VIEW_XWAYLAND\n\t\t\t&& container->view->wlr_xwayland_surface->window_id == *wid);\n}\n#endif\n\nstatic bool test_mark(struct sway_container *container, void *mark) {\n\tif (container->marks->length) {\n\t\treturn list_seq_find(container->marks,\n\t\t\t\t(int (*)(const void *, const void *))strcmp, mark) != -1;\n\t}\n\treturn false;\n}\n\nstruct cmd_results *cmd_swap(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"swap\", EXPECTED_AT_LEAST, 4))) {\n\t\treturn error;\n\t}\n\tif (!root->outputs->length) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t}\n\n\tif (strcasecmp(argv[0], \"container\") || strcasecmp(argv[1], \"with\")) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\n\tstruct sway_container *current = config->handler_context.container;\n\tstruct sway_container *other = NULL;\n\n\tchar *value = join_args(argv + 3, argc - 3);\n\tif (strcasecmp(argv[2], \"id\") == 0) {\n#if WLR_HAS_XWAYLAND\n\t\txcb_window_t id = strtol(value, NULL, 0);\n\t\tother = root_find_container(test_id, &id);\n#endif\n\t} else if (strcasecmp(argv[2], \"con_id\") == 0) {\n\t\tsize_t con_id = atoi(value);\n\t\tother = root_find_container(test_con_id, &con_id);\n\t} else if (strcasecmp(argv[2], \"mark\") == 0) {\n\t\tother = root_find_container(test_mark, value);\n\t} else {\n\t\tfree(value);\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected_syntax);\n\t}\n\n\tif (!other) {\n\t\terror = cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Failed to find %s '%s'\", argv[2], value);\n\t} else if (!current) {\n\t\terror = cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Can only swap with containers and views\");\n\t} else if (current == other) {\n\t\terror = cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Cannot swap a container with itself\");\n\t} else if (container_has_ancestor(current, other)\n\t\t\t|| container_has_ancestor(other, current)) {\n\t\terror = cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Cannot swap ancestor and descendant\");\n\t}\n\n\tfree(value);\n\n\tif (error) {\n\t\treturn error;\n\t}\n\n\tcontainer_swap(current, other);\n\n\tif (root->fullscreen_global) {\n\t\tarrange_root();\n\t} else {\n\t\tstruct sway_node *current_parent = node_get_parent(&current->node);\n\t\tstruct sway_node *other_parent = node_get_parent(&other->node);\n\t\tif (current_parent) {\n\t\t\tarrange_node(current_parent);\n\t\t}\n\t\tif (other_parent && current_parent != other_parent) {\n\t\t\tarrange_node(other_parent);\n\t\t}\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/swaybg_command.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *cmd_swaybg_command(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"swaybg_command\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tfree(config->swaybg_command);\n\tconfig->swaybg_command = NULL;\n\n\tchar *new_command = join_args(argv, argc);\n\tif (strcmp(new_command, \"-\") != 0) {\n\t\tconfig->swaybg_command = new_command;\n\t\tsway_log(SWAY_DEBUG, \"Using custom swaybg command: %s\",\n\t\t\t\tconfig->swaybg_command);\n\t} else {\n\t\tfree(new_command);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/swaynag_command.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *cmd_swaynag_command(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"swaynag_command\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tfree(config->swaynag_command);\n\tconfig->swaynag_command = NULL;\n\n\tchar *new_command = join_args(argv, argc);\n\tif (strcmp(new_command, \"-\") != 0) {\n\t\tconfig->swaynag_command = new_command;\n\t\tsway_log(SWAY_DEBUG, \"Using custom swaynag command: %s\",\n\t\t\t\tconfig->swaynag_command);\n\t} else {\n\t\tfree(new_command);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/tiling_drag.c",
    "content": "#include \"sway/commands.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_tiling_drag(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"tiling_drag\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tconfig->tiling_drag = parse_boolean(argv[0], config->tiling_drag);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/tiling_drag_threshold.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"log.h\"\n\nstruct cmd_results *cmd_tiling_drag_threshold(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"tiling_drag_threshold\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tchar *inv;\n\tint value = strtol(argv[0], &inv, 10);\n\tif (*inv != '\\0' || value < 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Invalid threshold specified\");\n\t}\n\n\tconfig->tiling_drag_threshold = value;\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/title_align.c",
    "content": "#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n\nstatic void arrange_title_bar_iterator(struct sway_container *con, void *data) {\n\tcontainer_arrange_title_bar(con);\n}\n\nstruct cmd_results *cmd_title_align(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"title_align\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tif (strcmp(argv[0], \"left\") == 0) {\n\t\tconfig->title_align = ALIGN_LEFT;\n\t} else if (strcmp(argv[0], \"center\") == 0) {\n\t\tconfig->title_align = ALIGN_CENTER;\n\t} else if (strcmp(argv[0], \"right\") == 0) {\n\t\tconfig->title_align = ALIGN_RIGHT;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'title_align left|center|right'\");\n\t}\n\n\troot_for_each_container(arrange_title_bar_iterator, NULL);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/title_format.c",
    "content": "#define _POSIX_C_SOURCE 200809L\n#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/view.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstruct cmd_results *cmd_title_format(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"title_format\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t \"Only valid containers can have a title_format\");\n\t}\n\tchar *format = join_args(argv, argc);\n\tif (container->title_format) {\n\t\tfree(container->title_format);\n\t}\n\tcontainer->title_format = format;\n\tif (container->view) {\n\t\tview_update_title(container->view, true);\n\t} else {\n\t\tcontainer_update_representation(container);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/titlebar_border_thickness.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"log.h\"\n\nstruct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"titlebar_border_thickness\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n\tchar *inv;\n\tint value = strtol(argv[0], &inv, 10);\n\tif (*inv != '\\0' || value < 0 || value > config->titlebar_v_padding) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid size specified\");\n\t}\n\n\tconfig->titlebar_border_thickness = value;\n\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tstruct sway_workspace *ws = output_get_active_workspace(output);\n\t\tif (!sway_assert(ws, \"Expected output to have a workspace\")) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Expected output to have a workspace\");\n\t\t}\n\t\tarrange_workspace(ws);\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/titlebar_padding.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"log.h\"\n\nstruct cmd_results *cmd_titlebar_padding(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"titlebar_padding\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tchar *inv;\n\tint h_value = strtol(argv[0], &inv, 10);\n\tif (*inv != '\\0' || h_value < 0 || h_value < config->titlebar_border_thickness) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid size specified\");\n\t}\n\n\tint v_value;\n\tif (argc == 1) {\n\t\tv_value = h_value;\n\t} else {\n\t\tv_value = strtol(argv[1], &inv, 10);\n\t\tif (*inv != '\\0' || v_value < 0 || v_value < config->titlebar_border_thickness) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"Invalid size specified\");\n\t\t}\n\t}\n\n\tconfig->titlebar_v_padding = v_value;\n\tconfig->titlebar_h_padding = h_value;\n\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tarrange_workspace(output_get_active_workspace(output));\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/unmark.c",
    "content": "#include <string.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic void remove_mark(struct sway_container *con) {\n\tcontainer_clear_marks(con);\n\tcontainer_update_marks(con);\n}\n\nstatic void remove_all_marks_iterator(struct sway_container *con, void *data) {\n\tremove_mark(con);\n}\n\n// unmark                  Remove all marks from all views\n// unmark foo              Remove single mark from whichever view has it\n// [criteria] unmark       Remove all marks from matched view\n// [criteria] unmark foo   Remove single mark from matched view\n\nstruct cmd_results *cmd_unmark(int argc, char **argv) {\n\t// Determine the container\n\tstruct sway_container *con = NULL;\n\tif (config->handler_context.node_overridden) {\n\t\tcon = config->handler_context.container;\n\t}\n\n\t// Determine the mark\n\tchar *mark = NULL;\n\tif (argc > 0) {\n\t\tmark = join_args(argv, argc);\n\t}\n\n\tif (con && mark) {\n\t\t// Remove the mark from the given container\n\t\tif (container_has_mark(con, mark)) {\n\t\t\tcontainer_find_and_unmark(mark);\n\t\t}\n\t} else if (con && !mark) {\n\t\t// Clear all marks from the given container\n\t\tremove_mark(con);\n\t} else if (!con && mark) {\n\t\t// Remove mark from whichever container has it\n\t\tcontainer_find_and_unmark(mark);\n\t} else {\n\t\t// Remove all marks from all containers\n\t\troot_for_each_container(remove_all_marks_iterator, NULL);\n\t}\n\tfree(mark);\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/urgent.c",
    "content": "#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_urgent(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"urgent\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tstruct sway_container *container = config->handler_context.container;\n\tif (!container) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"No current container\");\n\t}\n\tif (!container->view) {\n\t\treturn cmd_results_new(CMD_INVALID, \"Only views can be urgent\");\n\t}\n\tstruct sway_view *view = container->view;\n\n\tif (strcmp(argv[0], \"allow\") == 0) {\n\t\tview->allow_request_urgent = true;\n\t} else if (strcmp(argv[0], \"deny\") == 0) {\n\t\tview->allow_request_urgent = false;\n\t} else {\n\t\tview_set_urgent(view, parse_boolean(argv[0], view_is_urgent(view)));\n\t}\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/workspace.c",
    "content": "#include <ctype.h>\n#include <limits.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/workspace.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic struct workspace_config *workspace_config_find_or_create(char *ws_name) {\n\tstruct workspace_config *wsc = workspace_find_config(ws_name);\n\tif (wsc) {\n\t\treturn wsc;\n\t}\n\twsc = calloc(1, sizeof(struct workspace_config));\n\tif (!wsc) {\n\t\treturn NULL;\n\t}\n\twsc->workspace = strdup(ws_name);\n\twsc->outputs = create_list();\n\twsc->gaps_inner = INT_MIN;\n\twsc->gaps_outer.top = INT_MIN;\n\twsc->gaps_outer.right = INT_MIN;\n\twsc->gaps_outer.bottom = INT_MIN;\n\twsc->gaps_outer.left = INT_MIN;\n\tlist_add(config->workspace_configs, wsc);\n\treturn wsc;\n}\n\nvoid free_workspace_config(struct workspace_config *wsc) {\n\tfree(wsc->workspace);\n\tlist_free_items_and_destroy(wsc->outputs);\n\tfree(wsc);\n}\n\nstatic void prevent_invalid_outer_gaps(struct workspace_config *wsc) {\n\tif (wsc->gaps_outer.top != INT_MIN &&\n\t\t\twsc->gaps_outer.top < -wsc->gaps_inner) {\n\t\twsc->gaps_outer.top = -wsc->gaps_inner;\n\t}\n\tif (wsc->gaps_outer.right != INT_MIN &&\n\t\t\twsc->gaps_outer.right < -wsc->gaps_inner) {\n\t\twsc->gaps_outer.right = -wsc->gaps_inner;\n\t}\n\tif (wsc->gaps_outer.bottom != INT_MIN &&\n\t\t\twsc->gaps_outer.bottom < -wsc->gaps_inner) {\n\t\twsc->gaps_outer.bottom = -wsc->gaps_inner;\n\t}\n\tif (wsc->gaps_outer.left != INT_MIN &&\n\t\t\twsc->gaps_outer.left < -wsc->gaps_inner) {\n\t\twsc->gaps_outer.left = -wsc->gaps_inner;\n\t}\n}\n\nstatic struct cmd_results *cmd_workspace_gaps(int argc, char **argv,\n\t\tint gaps_location) {\n\tconst char expected[] = \"Expected 'workspace <name> gaps \"\n\t\t\"inner|outer|horizontal|vertical|top|right|bottom|left <px>'\";\n\tif (gaps_location == 0) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected);\n\t}\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"workspace\", EXPECTED_EQUAL_TO,\n\t\t\t\t\tgaps_location + 3))) {\n\t\treturn error;\n\t}\n\tchar *ws_name = join_args(argv, argc - 3);\n\tstruct workspace_config *wsc = workspace_config_find_or_create(ws_name);\n\tfree(ws_name);\n\tif (!wsc) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Unable to allocate workspace output\");\n\t}\n\n\tchar *end;\n\tint amount = strtol(argv[gaps_location + 2], &end, 10);\n\tif (strlen(end)) {\n\t\treturn cmd_results_new(CMD_FAILURE, \"%s\", expected);\n\t}\n\n\tbool valid = false;\n\tchar *type = argv[gaps_location + 1];\n\tif (!strcasecmp(type, \"inner\")) {\n\t\tvalid = true;\n\t\twsc->gaps_inner = (amount >= 0) ? amount : 0;\n\t} else {\n\t\tif (!strcasecmp(type, \"outer\") || !strcasecmp(type, \"vertical\")\n\t\t\t\t|| !strcasecmp(type, \"top\")) {\n\t\t\tvalid = true;\n\t\t\twsc->gaps_outer.top = amount;\n\t\t}\n\t\tif (!strcasecmp(type, \"outer\") || !strcasecmp(type, \"horizontal\")\n\t\t\t\t|| !strcasecmp(type, \"right\")) {\n\t\t\tvalid = true;\n\t\t\twsc->gaps_outer.right = amount;\n\t\t}\n\t\tif (!strcasecmp(type, \"outer\") || !strcasecmp(type, \"vertical\")\n\t\t\t\t|| !strcasecmp(type, \"bottom\")) {\n\t\t\tvalid = true;\n\t\t\twsc->gaps_outer.bottom = amount;\n\t\t}\n\t\tif (!strcasecmp(type, \"outer\") || !strcasecmp(type, \"horizontal\")\n\t\t\t\t|| !strcasecmp(type, \"left\")) {\n\t\t\tvalid = true;\n\t\t\twsc->gaps_outer.left = amount;\n\t\t}\n\t}\n\tif (!valid) {\n\t\treturn cmd_results_new(CMD_INVALID, \"%s\", expected);\n\t}\n\n\t// Prevent invalid gaps configurations.\n\tif (wsc->gaps_inner != INT_MIN && wsc->gaps_inner < 0) {\n\t\twsc->gaps_inner = 0;\n\t}\n\tprevent_invalid_outer_gaps(wsc);\n\n\treturn error;\n}\n\nstruct cmd_results *cmd_workspace(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"workspace\", EXPECTED_AT_LEAST, 1))) {\n\t\treturn error;\n\t}\n\n\tint output_location = -1;\n\tint gaps_location = -1;\n\n\tfor (int i = 0; i < argc; ++i) {\n\t\tif (strcasecmp(argv[i], \"output\") == 0) {\n\t\t\toutput_location = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\tfor (int i = 0; i < argc; ++i) {\n\t\tif (strcasecmp(argv[i], \"gaps\") == 0) {\n\t\t\tgaps_location = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (output_location == 0) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"Expected 'workspace <name> output <output>'\");\n\t} else if (output_location > 0) {\n\t\tif ((error = checkarg(argc, \"workspace\", EXPECTED_AT_LEAST,\n\t\t\t\t\t\toutput_location + 2))) {\n\t\t\treturn error;\n\t\t}\n\t\tchar *ws_name = join_args(argv, output_location);\n\t\tstruct workspace_config *wsc = workspace_config_find_or_create(ws_name);\n\t\tfree(ws_name);\n\t\tif (!wsc) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\t\"Unable to allocate workspace output\");\n\t\t}\n\t\tfor (int i = output_location + 1; i < argc; ++i) {\n\t\t\tlist_add(wsc->outputs, strdup(argv[i]));\n\t\t}\n\t} else if (gaps_location >= 0) {\n\t\tif ((error = cmd_workspace_gaps(argc, argv, gaps_location))) {\n\t\t\treturn error;\n\t\t}\n\t} else {\n\t\tif (config->reading || !config->active) {\n\t\t\treturn cmd_results_new(CMD_DEFER, NULL);\n\t\t} else if (!root->outputs->length) {\n\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Can't run this command while there's no outputs connected.\");\n\t\t}\n\n\t\tif (root->fullscreen_global) {\n\t\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"Can't switch workspaces while fullscreen global\");\n\t\t}\n\n\t\tbool auto_back_and_forth = true;\n\t\twhile (strcasecmp(argv[0], \"--no-auto-back-and-forth\") == 0) {\n\t\t\tauto_back_and_forth = false;\n\t\t\tif ((error = checkarg(--argc, \"workspace\", EXPECTED_AT_LEAST, 1))) {\n\t\t\t\treturn error;\n\t\t\t}\n\t\t\t++argv;\n\t\t}\n\n\t\tstruct sway_seat *seat = config->handler_context.seat;\n\n\t\tstruct sway_workspace *ws = NULL;\n\t\tif (strcasecmp(argv[0], \"number\") == 0) {\n\t\t\tif (argc < 2) {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Expected workspace number\");\n\t\t\t}\n\t\t\tif (!isdigit(argv[1][0])) {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"Invalid workspace number '%s'\", argv[1]);\n\t\t\t}\n\t\t\tif (!(ws = workspace_by_number(argv[1]))) {\n\t\t\t\tchar *name = join_args(argv + 1, argc - 1);\n\t\t\t\tws = workspace_create(NULL, name);\n\t\t\t\tfree(name);\n\t\t\t}\n\t\t\tif (ws && auto_back_and_forth) {\n\t\t\t\tws = workspace_auto_back_and_forth(ws);\n\t\t\t}\n\t\t} else if (strcasecmp(argv[0], \"next\") == 0 ||\n\t\t\t\tstrcasecmp(argv[0], \"prev\") == 0 ||\n\t\t\t\tstrcasecmp(argv[0], \"next_on_output\") == 0 ||\n\t\t\t\tstrcasecmp(argv[0], \"prev_on_output\") == 0 ||\n\t\t\t\tstrcasecmp(argv[0], \"current\") == 0) {\n\t\t\tws = workspace_by_name(argv[0]);\n\t\t} else if (strcasecmp(argv[0], \"back_and_forth\") == 0) {\n\t\t\tif (!seat->prev_workspace_name) {\n\t\t\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\t\"There is no previous workspace\");\n\t\t\t}\n\t\t\tif (!(ws = workspace_by_name(argv[0]))) {\n\t\t\t\tws = workspace_create(NULL, seat->prev_workspace_name);\n\t\t\t}\n\t\t} else {\n\t\t\tchar *name = join_args(argv, argc);\n\t\t\tif (!(ws = workspace_by_name(name))) {\n\t\t\t\tws = workspace_create(NULL, name);\n\t\t\t}\n\t\t\tfree(name);\n\t\t\tif (ws && auto_back_and_forth) {\n\t\t\t\tws = workspace_auto_back_and_forth(ws);\n\t\t\t}\n\t\t}\n\t\tif (!ws) {\n\t\t\treturn cmd_results_new(CMD_FAILURE, \"No workspace to switch to\");\n\t\t}\n\t\tworkspace_switch(ws);\n\t\tseat_consider_warp_to_focus(seat);\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/workspace_layout.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n\nstruct cmd_results *cmd_workspace_layout(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"workspace_layout\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tif (strcasecmp(argv[0], \"default\") == 0) {\n\t\tconfig->default_layout = L_NONE;\n\t} else if (strcasecmp(argv[0], \"stacking\") == 0) {\n\t\tconfig->default_layout = L_STACKED;\n\t} else if (strcasecmp(argv[0], \"tabbed\") == 0) {\n\t\tconfig->default_layout = L_TABBED;\n\t} else {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Expected 'workspace_layout <default|stacking|tabbed>'\");\n\t}\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/ws_auto_back_and_forth.c",
    "content": "#include <string.h>\n#include <strings.h>\n#include \"sway/commands.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"workspace_auto_back_and_forth\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\tconfig->auto_back_and_forth =\n\t\tparse_boolean(argv[0], config->auto_back_and_forth);\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands/xwayland.c",
    "content": "#include \"sway/config.h\"\n#include \"log.h\"\n#include \"sway/commands.h\"\n#include \"sway/server.h\"\n#include \"util.h\"\n\nstruct cmd_results *cmd_xwayland(int argc, char **argv) {\n\tstruct cmd_results *error = NULL;\n\tif ((error = checkarg(argc, \"xwayland\", EXPECTED_EQUAL_TO, 1))) {\n\t\treturn error;\n\t}\n\n#ifdef WLR_HAS_XWAYLAND\n\tenum xwayland_mode xwayland;\n\tif (strcmp(argv[0], \"force\") == 0) {\n\t\txwayland = XWAYLAND_MODE_IMMEDIATE;\n\t} else if (parse_boolean(argv[0], true)) {\n\t\txwayland = XWAYLAND_MODE_LAZY;\n\t} else {\n\t\txwayland = XWAYLAND_MODE_DISABLED;\n\t}\n\n\t// config->xwayland is reset to the previous value on reload in\n\t// load_main_config()\n\tif (config->reloading && config->xwayland != xwayland) {\n\t\treturn cmd_results_new(CMD_FAILURE,\n\t\t\t\t\"xwayland can only be enabled/disabled at launch\");\n\t}\n\tconfig->xwayland = xwayland;\n#else\n\tsway_log(SWAY_INFO, \"Ignoring `xwayland` command, \"\n\t\t\"sway hasn't been built with Xwayland support\");\n#endif\n\n\treturn cmd_results_new(CMD_SUCCESS, NULL);\n}\n"
  },
  {
    "path": "sway/commands.c",
    "content": "#include <ctype.h>\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <stdio.h>\n#include <json.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/criteria.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/view.h\"\n#include \"stringop.h\"\n#include \"log.h\"\n\n// Returns error object, or NULL if check succeeds.\nstruct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {\n\tconst char *error_name = NULL;\n\tswitch (type) {\n\tcase EXPECTED_AT_LEAST:\n\t\tif (argc < val) {\n\t\t\terror_name = \"at least \";\n\t\t}\n\t\tbreak;\n\tcase EXPECTED_AT_MOST:\n\t\tif (argc > val) {\n\t\t\terror_name = \"at most \";\n\t\t}\n\t\tbreak;\n\tcase EXPECTED_EQUAL_TO:\n\t\tif (argc != val) {\n\t\t\terror_name = \"\";\n\t\t}\n\t}\n\treturn error_name ?\n\t\tcmd_results_new(CMD_INVALID, \"Invalid %s command \"\n\t\t\t\t\"(expected %s%d argument%s, got %d)\",\n\t\t\t\tname, error_name, val, val != 1 ? \"s\" : \"\", argc)\n\t\t: NULL;\n}\n\n/* Keep alphabetized */\nstatic const struct cmd_handler handlers[] = {\n\t{ \"assign\", cmd_assign },\n\t{ \"bar\", cmd_bar },\n\t{ \"bindcode\", cmd_bindcode },\n\t{ \"bindgesture\", cmd_bindgesture },\n\t{ \"bindswitch\", cmd_bindswitch },\n\t{ \"bindsym\", cmd_bindsym },\n\t{ \"client.background\", cmd_client_noop },\n\t{ \"client.focused\", cmd_client_focused },\n\t{ \"client.focused_inactive\", cmd_client_focused_inactive },\n\t{ \"client.focused_tab_title\", cmd_client_focused_tab_title },\n\t{ \"client.placeholder\", cmd_client_noop },\n\t{ \"client.unfocused\", cmd_client_unfocused },\n\t{ \"client.urgent\", cmd_client_urgent },\n\t{ \"default_border\", cmd_default_border },\n\t{ \"default_floating_border\", cmd_default_floating_border },\n\t{ \"exec\", cmd_exec },\n\t{ \"exec_always\", cmd_exec_always },\n\t{ \"floating_maximum_size\", cmd_floating_maximum_size },\n\t{ \"floating_minimum_size\", cmd_floating_minimum_size },\n\t{ \"floating_modifier\", cmd_floating_modifier },\n\t{ \"focus\", cmd_focus },\n\t{ \"focus_follows_mouse\", cmd_focus_follows_mouse },\n\t{ \"focus_on_window_activation\", cmd_focus_on_window_activation },\n\t{ \"focus_wrapping\", cmd_focus_wrapping },\n\t{ \"font\", cmd_font },\n\t{ \"for_window\", cmd_for_window },\n\t{ \"force_display_urgency_hint\", cmd_force_display_urgency_hint },\n\t{ \"force_focus_wrapping\", cmd_force_focus_wrapping },\n\t{ \"fullscreen\", cmd_fullscreen },\n\t{ \"gaps\", cmd_gaps },\n\t{ \"hide_edge_borders\", cmd_hide_edge_borders },\n\t{ \"input\", cmd_input },\n\t{ \"mode\", cmd_mode },\n\t{ \"mouse_warping\", cmd_mouse_warping },\n\t{ \"new_float\", cmd_new_float },\n\t{ \"new_window\", cmd_new_window },\n\t{ \"no_focus\", cmd_no_focus },\n\t{ \"output\", cmd_output },\n\t{ \"popup_during_fullscreen\", cmd_popup_during_fullscreen },\n\t{ \"seat\", cmd_seat },\n\t{ \"set\", cmd_set },\n\t{ \"show_marks\", cmd_show_marks },\n\t{ \"smart_borders\", cmd_smart_borders },\n\t{ \"smart_gaps\", cmd_smart_gaps },\n\t{ \"tiling_drag\", cmd_tiling_drag },\n\t{ \"tiling_drag_threshold\", cmd_tiling_drag_threshold },\n\t{ \"title_align\", cmd_title_align },\n\t{ \"titlebar_border_thickness\", cmd_titlebar_border_thickness },\n\t{ \"titlebar_padding\", cmd_titlebar_padding },\n\t{ \"unbindcode\", cmd_unbindcode },\n\t{ \"unbindgesture\", cmd_unbindgesture },\n\t{ \"unbindswitch\", cmd_unbindswitch },\n\t{ \"unbindsym\", cmd_unbindsym },\n\t{ \"workspace\", cmd_workspace },\n\t{ \"workspace_auto_back_and_forth\", cmd_ws_auto_back_and_forth },\n};\n\n/* Config-time only commands. Keep alphabetized */\nstatic const struct cmd_handler config_handlers[] = {\n\t{ \"default_orientation\", cmd_default_orientation },\n\t{ \"include\", cmd_include },\n\t{ \"primary_selection\", cmd_primary_selection },\n\t{ \"swaybg_command\", cmd_swaybg_command },\n\t{ \"swaynag_command\", cmd_swaynag_command },\n\t{ \"workspace_layout\", cmd_workspace_layout },\n\t{ \"xwayland\", cmd_xwayland },\n};\n\n/* Runtime-only commands. Keep alphabetized */\nstatic const struct cmd_handler command_handlers[] = {\n\t{ \"allow_tearing\", cmd_allow_tearing },\n\t{ \"border\", cmd_border },\n\t{ \"create_output\", cmd_create_output },\n\t{ \"exit\", cmd_exit },\n\t{ \"floating\", cmd_floating },\n\t{ \"fullscreen\", cmd_fullscreen },\n\t{ \"inhibit_idle\", cmd_inhibit_idle },\n\t{ \"kill\", cmd_kill },\n\t{ \"layout\", cmd_layout },\n\t{ \"mark\", cmd_mark },\n\t{ \"max_render_time\", cmd_max_render_time },\n\t{ \"move\", cmd_move },\n\t{ \"nop\", cmd_nop },\n\t{ \"opacity\", cmd_opacity },\n\t{ \"reload\", cmd_reload },\n\t{ \"rename\", cmd_rename },\n\t{ \"resize\", cmd_resize },\n\t{ \"scratchpad\", cmd_scratchpad },\n\t{ \"shortcuts_inhibitor\", cmd_shortcuts_inhibitor },\n\t{ \"split\", cmd_split },\n\t{ \"splith\", cmd_splith },\n\t{ \"splitt\", cmd_splitt },\n\t{ \"splitv\", cmd_splitv },\n\t{ \"sticky\", cmd_sticky },\n\t{ \"swap\", cmd_swap },\n\t{ \"title_format\", cmd_title_format },\n\t{ \"unmark\", cmd_unmark },\n\t{ \"urgent\", cmd_urgent },\n};\n\nstatic int handler_compare(const void *_a, const void *_b) {\n\tconst struct cmd_handler *a = _a;\n\tconst struct cmd_handler *b = _b;\n\treturn strcasecmp(a->command, b->command);\n}\n\nconst struct cmd_handler *find_handler(const char *line,\n\t\tconst struct cmd_handler *handlers, size_t handlers_size) {\n\tif (!handlers || !handlers_size) {\n\t\treturn NULL;\n\t}\n\tconst struct cmd_handler query = { .command = line };\n\treturn bsearch(&query, handlers,\n\t\t\thandlers_size / sizeof(struct cmd_handler),\n\t\t\tsizeof(struct cmd_handler), handler_compare);\n}\n\nstatic const struct cmd_handler *find_handler_ex(char *line,\n\t\tconst struct cmd_handler *config_handlers, size_t config_handlers_size,\n\t\tconst struct cmd_handler *command_handlers, size_t command_handlers_size,\n\t\tconst struct cmd_handler *handlers, size_t handlers_size) {\n\tconst struct cmd_handler *handler = NULL;\n\tif (config->reading) {\n\t\thandler = find_handler(line, config_handlers, config_handlers_size);\n\t} else if (config->active) {\n\t\thandler = find_handler(line, command_handlers, command_handlers_size);\n\t}\n\treturn handler ? handler : find_handler(line, handlers, handlers_size);\n}\n\nstatic const struct cmd_handler *find_core_handler(char *line) {\n\treturn find_handler_ex(line, config_handlers, sizeof(config_handlers),\n\t\t\tcommand_handlers, sizeof(command_handlers),\n\t\t\thandlers, sizeof(handlers));\n}\n\nstatic void set_config_node(struct sway_node *node, bool node_overridden) {\n\tconfig->handler_context.node = node;\n\tconfig->handler_context.container = NULL;\n\tconfig->handler_context.workspace = NULL;\n\tconfig->handler_context.node_overridden = node_overridden;\n\n\tif (node == NULL) {\n\t\treturn;\n\t}\n\n\tswitch (node->type) {\n\tcase N_CONTAINER:\n\t\tconfig->handler_context.container = node->sway_container;\n\t\tconfig->handler_context.workspace = node->sway_container->pending.workspace;\n\t\tbreak;\n\tcase N_WORKSPACE:\n\t\tconfig->handler_context.workspace = node->sway_workspace;\n\t\tbreak;\n\tcase N_ROOT:\n\tcase N_OUTPUT:\n\t\tbreak;\n\t}\n}\n\nlist_t *execute_command(char *_exec, struct sway_seat *seat,\n\t\tstruct sway_container *con) {\n\tchar *cmd;\n\tchar matched_delim = ';';\n\tlist_t *containers = NULL;\n\tbool using_criteria = false;\n\n\tif (seat == NULL) {\n\t\t// passing a NULL seat means we just pick the default seat\n\t\tseat = input_manager_get_default_seat();\n\t\tif (!sway_assert(seat, \"could not find a seat to run the command on\")) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tchar *exec = strdup(_exec);\n\tchar *head = exec;\n\tlist_t *res_list = create_list();\n\n\tif (!res_list || !exec) {\n\t\treturn NULL;\n\t}\n\n\tconfig->handler_context.seat = seat;\n\n\tdo {\n\t\tfor (; isspace(*head); ++head) {}\n\t\t// Extract criteria (valid for this command list only).\n\t\tif (matched_delim == ';') {\n\t\t\tusing_criteria = false;\n\t\t\tif (*head == '[') {\n\t\t\t\tchar *error = NULL;\n\t\t\t\tstruct criteria *criteria = criteria_parse(head, &error);\n\t\t\t\tif (!criteria) {\n\t\t\t\t\tlist_add(res_list,\n\t\t\t\t\t\t\tcmd_results_new(CMD_INVALID, \"%s\", error));\n\t\t\t\t\tfree(error);\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tlist_free(containers);\n\t\t\t\tcontainers = criteria_get_containers(criteria);\n\t\t\t\thead += strlen(criteria->raw);\n\t\t\t\tcriteria_destroy(criteria);\n\t\t\t\tusing_criteria = true;\n\t\t\t\t// Skip leading whitespace\n\t\t\t\tfor (; isspace(*head); ++head) {}\n\t\t\t}\n\t\t}\n\t\t// Split command list\n\t\tcmd = argsep(&head, \";,\", &matched_delim);\n\t\tfor (; isspace(*cmd); ++cmd) {}\n\n\t\tif (strcmp(cmd, \"\") == 0) {\n\t\t\tsway_log(SWAY_INFO, \"Ignoring empty command.\");\n\t\t\tcontinue;\n\t\t}\n\t\tsway_log(SWAY_INFO, \"Handling command '%s'\", cmd);\n\t\t//TODO better handling of argv\n\t\tint argc;\n\t\tchar **argv = split_args(cmd, &argc);\n\t\tif (strcmp(argv[0], \"exec\") != 0 &&\n\t\t\t\tstrcmp(argv[0], \"exec_always\") != 0 &&\n\t\t\t\tstrcmp(argv[0], \"mode\") != 0) {\n\t\t\tfor (int i = 1; i < argc; ++i) {\n\t\t\t\tif (*argv[i] == '\\\"' || *argv[i] == '\\'') {\n\t\t\t\t\tstrip_quotes(argv[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst struct cmd_handler *handler = find_core_handler(argv[0]);\n\t\tif (!handler) {\n\t\t\tlist_add(res_list, cmd_results_new(CMD_INVALID,\n\t\t\t\t\t\"Unknown/invalid command '%s'\", argv[0]));\n\t\t\tfree_argv(argc, argv);\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\t// Var replacement, for all but first argument of set\n\t\tfor (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {\n\t\t\targv[i] = do_var_replacement(argv[i]);\n\t\t}\n\n\n\t\tif (!using_criteria) {\n\t\t\tif (con) {\n\t\t\t\tset_config_node(&con->node, true);\n\t\t\t} else {\n\t\t\t\tset_config_node(seat_get_focus_inactive(seat, &root->node),\n\t\t\t\t\t\tfalse);\n\t\t\t}\n\t\t\tstruct cmd_results *res = handler->handle(argc-1, argv+1);\n\t\t\tlist_add(res_list, res);\n\t\t\tif (res->status == CMD_INVALID) {\n\t\t\t\tfree_argv(argc, argv);\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t} else if (containers->length == 0) {\n\t\t\tlist_add(res_list,\n\t\t\t\t\tcmd_results_new(CMD_FAILURE, \"No matching node.\"));\n\t\t} else {\n\t\t\tstruct cmd_results *fail_res = NULL;\n\t\t\tfor (int i = 0; i < containers->length; ++i) {\n\t\t\t\tstruct sway_container *container = containers->items[i];\n\t\t\t\tset_config_node(&container->node, true);\n\t\t\t\tstruct cmd_results *res = handler->handle(argc-1, argv+1);\n\t\t\t\tif (res->status == CMD_SUCCESS) {\n\t\t\t\t\tfree_cmd_results(res);\n\t\t\t\t} else {\n\t\t\t\t\t// last failure will take precedence\n\t\t\t\t\tif (fail_res) {\n\t\t\t\t\t\tfree_cmd_results(fail_res);\n\t\t\t\t\t}\n\t\t\t\t\tfail_res = res;\n\t\t\t\t\tif (res->status == CMD_INVALID) {\n\t\t\t\t\t\tlist_add(res_list, fail_res);\n\t\t\t\t\t\tfree_argv(argc, argv);\n\t\t\t\t\t\tgoto cleanup;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tlist_add(res_list,\n\t\t\t\t\tfail_res ? fail_res : cmd_results_new(CMD_SUCCESS, NULL));\n\t\t}\n\t\tfree_argv(argc, argv);\n\t} while(head);\ncleanup:\n\tfree(exec);\n\tlist_free(containers);\n\treturn res_list;\n}\n\n// this is like execute_command above, except:\n// 1) it ignores empty commands (empty lines)\n// 2) it does variable substitution\n// 3) it doesn't split commands (because the multiple commands are supposed to\n//\t  be chained together)\n// 4) execute_command handles all state internally while config_command has\n// some state handled outside (notably the block mode, in read_config)\nstruct cmd_results *config_command(char *exec, char **new_block) {\n\tstruct cmd_results *results = NULL;\n\tint argc;\n\tchar **argv = split_args(exec, &argc);\n\n\t// Check for empty lines\n\tif (!argc) {\n\t\tresults = cmd_results_new(CMD_SUCCESS, NULL);\n\t\tgoto cleanup;\n\t}\n\n\t// Check for the start of a block\n\tif (argc > 1 && strcmp(argv[argc - 1], \"{\") == 0) {\n\t\t*new_block = join_args(argv, argc - 1);\n\t\tresults = cmd_results_new(CMD_BLOCK, NULL);\n\t\tgoto cleanup;\n\t}\n\n\t// Check for the end of a block\n\tif (strcmp(argv[argc - 1], \"}\") == 0) {\n\t\tresults = cmd_results_new(CMD_BLOCK_END, NULL);\n\t\tgoto cleanup;\n\t}\n\n\t// Make sure the command is not stored in a variable\n\tif (*argv[0] == '$') {\n\t\targv[0] = do_var_replacement(argv[0]);\n\t\tchar *temp = join_args(argv, argc);\n\t\tfree_argv(argc, argv);\n\t\targv = split_args(temp, &argc);\n\t\tfree(temp);\n\t\tif (!argc) {\n\t\t\tresults = cmd_results_new(CMD_SUCCESS, NULL);\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\t// Determine the command handler\n\tsway_log(SWAY_INFO, \"Config command: %s\", exec);\n\tconst struct cmd_handler *handler = find_core_handler(argv[0]);\n\tif (!handler || !handler->handle) {\n\t\tif (handler) {\n\t\t\tresults = cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Command '%s' is shimmed, but unimplemented\", argv[0]);\n\t\t} else {\n\t\t\tresults = cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Unknown/invalid command '%s'\", argv[0]);\n\t\t}\n\t\tgoto cleanup;\n\t}\n\n\t// Do variable replacement\n\tif (handler->handle == cmd_set && argc > 1 && *argv[1] == '$') {\n\t\t// Escape the variable name so it does not get replaced by one shorter\n\t\tchar *temp = calloc(1, strlen(argv[1]) + 2);\n\t\ttemp[0] = '$';\n\t\tstrcpy(&temp[1], argv[1]);\n\t\tfree(argv[1]);\n\t\targv[1] = temp;\n\t}\n\tchar *command = do_var_replacement(join_args(argv, argc));\n\tsway_log(SWAY_INFO, \"After replacement: %s\", command);\n\tfree_argv(argc, argv);\n\targv = split_args(command, &argc);\n\tfree(command);\n\n\t// Strip quotes and unescape the string\n\tfor (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {\n\t\tif (handler->handle != cmd_exec && handler->handle != cmd_exec_always\n\t\t\t\t&& handler->handle != cmd_mode\n\t\t\t\t&& handler->handle != cmd_bindsym\n\t\t\t\t&& handler->handle != cmd_bindcode\n\t\t\t\t&& handler->handle != cmd_bindswitch\n\t\t\t\t&& handler->handle != cmd_bindgesture\n\t\t\t\t&& handler->handle != cmd_set\n\t\t\t\t&& handler->handle != cmd_for_window\n\t\t\t\t&& (*argv[i] == '\\\"' || *argv[i] == '\\'')) {\n\t\t\tstrip_quotes(argv[i]);\n\t\t}\n\t\tunescape_string(argv[i]);\n\t}\n\n\t// Run command\n\tresults = handler->handle(argc - 1, argv + 1);\n\ncleanup:\n\tfree_argv(argc, argv);\n\treturn results;\n}\n\nstruct cmd_results *config_subcommand(char **argv, int argc,\n\t\tconst struct cmd_handler *handlers, size_t handlers_size) {\n\tchar *command = join_args(argv, argc);\n\tsway_log(SWAY_DEBUG, \"Subcommand: %s\", command);\n\tfree(command);\n\n\tconst struct cmd_handler *handler = find_handler(argv[0], handlers,\n\t\t\thandlers_size);\n\tif (!handler) {\n\t\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\t\"Unknown/invalid command '%s'\", argv[0]);\n\t}\n\tif (handler->handle) {\n\t\treturn handler->handle(argc - 1, argv + 1);\n\t}\n\treturn cmd_results_new(CMD_INVALID,\n\t\t\t\"The command '%s' is shimmed, but unimplemented\", argv[0]);\n}\n\nstruct cmd_results *config_commands_command(char *exec) {\n\tstruct cmd_results *results = NULL;\n\tint argc;\n\tchar **argv = split_args(exec, &argc);\n\tif (!argc) {\n\t\tresults = cmd_results_new(CMD_SUCCESS, NULL);\n\t\tgoto cleanup;\n\t}\n\n\t// Find handler for the command this is setting a policy for\n\tchar *cmd = argv[0];\n\n\tif (strcmp(cmd, \"}\") == 0) {\n\t\tresults = cmd_results_new(CMD_BLOCK_END, NULL);\n\t\tgoto cleanup;\n\t}\n\n\tconst struct cmd_handler *handler = find_handler(cmd, NULL, 0);\n\tif (!handler && strcmp(cmd, \"*\") != 0) {\n\t\tresults = cmd_results_new(CMD_INVALID,\n\t\t\t\"Unknown/invalid command '%s'\", cmd);\n\t\tgoto cleanup;\n\t}\n\n\tresults = cmd_results_new(CMD_SUCCESS, NULL);\n\ncleanup:\n\tfree_argv(argc, argv);\n\treturn results;\n}\n\nstruct cmd_results *cmd_results_new(enum cmd_status status,\n\t\tconst char *format, ...) {\n\tstruct cmd_results *results = malloc(sizeof(struct cmd_results));\n\tif (!results) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate command results\");\n\t\treturn NULL;\n\t}\n\tresults->status = status;\n\tif (format) {\n\t\tva_list args;\n\t\tva_start(args, format);\n\t\tresults->error = vformat_str(format, args);\n\t\tva_end(args);\n\t} else {\n\t\tresults->error = NULL;\n\t}\n\treturn results;\n}\n\nvoid free_cmd_results(struct cmd_results *results) {\n\tif (results->error) {\n\t\tfree(results->error);\n\t}\n\tfree(results);\n}\n\nchar *cmd_results_to_json(list_t *res_list) {\n\tjson_object *result_array = json_object_new_array();\n\tfor (int i = 0; i < res_list->length; ++i) {\n\t\tstruct cmd_results *results = res_list->items[i];\n\t\tjson_object *root = json_object_new_object();\n\t\tjson_object_object_add(root, \"success\",\n\t\t\t\tjson_object_new_boolean(results->status == CMD_SUCCESS));\n\t\tif (results->error) {\n\t\t\tjson_object_object_add(root, \"parse_error\",\n\t\t\t\t\tjson_object_new_boolean(results->status == CMD_INVALID));\n\t\t\tjson_object_object_add(\n\t\t\t\t\troot, \"error\", json_object_new_string(results->error));\n\t\t}\n\t\tjson_object_array_add(result_array, root);\n\t}\n\tconst char *json = json_object_to_json_string(result_array);\n\tchar *res = strdup(json);\n\tjson_object_put(result_array);\n\treturn res;\n}\n"
  },
  {
    "path": "sway/config/bar.c",
    "content": "#include <signal.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <strings.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <wordexp.h>\n#include \"sway/config.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"config.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nvoid free_bar_binding(struct bar_binding *binding) {\n\tif (!binding) {\n\t\treturn;\n\t}\n\tfree(binding->command);\n\tfree(binding);\n}\n\nvoid free_bar_config(struct bar_config *bar) {\n\tif (!bar) {\n\t\treturn;\n\t}\n\tfree(bar->id);\n\tfree(bar->mode);\n\tfree(bar->position);\n\tfree(bar->hidden_state);\n\tfree(bar->status_command);\n\tfree(bar->swaybar_command);\n\tfree(bar->font);\n\tfree(bar->separator_symbol);\n\tif (bar->bindings) {\n\t\tfor (int i = 0; i < bar->bindings->length; i++) {\n\t\t\tfree_bar_binding(bar->bindings->items[i]);\n\t\t}\n\t}\n\tlist_free(bar->bindings);\n\tlist_free_items_and_destroy(bar->outputs);\n\tif (bar->client != NULL) {\n\t\twl_client_destroy(bar->client);\n\t}\n\tfree(bar->colors.background);\n\tfree(bar->colors.statusline);\n\tfree(bar->colors.separator);\n\tfree(bar->colors.focused_background);\n\tfree(bar->colors.focused_statusline);\n\tfree(bar->colors.focused_separator);\n\tfree(bar->colors.focused_workspace_border);\n\tfree(bar->colors.focused_workspace_bg);\n\tfree(bar->colors.focused_workspace_text);\n\tfree(bar->colors.active_workspace_border);\n\tfree(bar->colors.active_workspace_bg);\n\tfree(bar->colors.active_workspace_text);\n\tfree(bar->colors.inactive_workspace_border);\n\tfree(bar->colors.inactive_workspace_bg);\n\tfree(bar->colors.inactive_workspace_text);\n\tfree(bar->colors.urgent_workspace_border);\n\tfree(bar->colors.urgent_workspace_bg);\n\tfree(bar->colors.urgent_workspace_text);\n\tfree(bar->colors.binding_mode_border);\n\tfree(bar->colors.binding_mode_bg);\n\tfree(bar->colors.binding_mode_text);\n#if HAVE_TRAY\n\tlist_free_items_and_destroy(bar->tray_outputs);\n\tfree(bar->icon_theme);\n\n\tstruct tray_binding *tray_bind = NULL, *tmp_tray_bind = NULL;\n\twl_list_for_each_safe(tray_bind, tmp_tray_bind, &bar->tray_bindings, link) {\n\t\twl_list_remove(&tray_bind->link);\n\t\tfree(tray_bind);\n\t}\n#endif\n\tfree(bar);\n}\n\nstruct bar_config *default_bar_config(void) {\n\tstruct bar_config *bar = NULL;\n\tbar = calloc(1, sizeof(struct bar_config));\n\tif (!bar) {\n\t\treturn NULL;\n\t}\n\tbar->outputs = NULL;\n\tbar->position = strdup(\"bottom\");\n\tbar->pango_markup = PANGO_MARKUP_DEFAULT;\n\tbar->swaybar_command = NULL;\n\tbar->font = NULL;\n\tbar->height = 0;\n\tbar->workspace_buttons = true;\n\tbar->wrap_scroll = false;\n\tbar->separator_symbol = NULL;\n\tbar->strip_workspace_numbers = false;\n\tbar->strip_workspace_name = false;\n\tbar->binding_mode_indicator = true;\n\tbar->verbose = false;\n\tbar->modifier = get_modifier_mask_by_name(\"Mod4\");\n\tbar->status_padding = 1;\n\tbar->status_edge_padding = 3;\n\tbar->workspace_min_width = 0;\n\tif (!(bar->mode = strdup(\"dock\"))) {\n\t       goto cleanup;\n\t}\n\tif (!(bar->hidden_state = strdup(\"hide\"))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->bindings = create_list())) {\n\t\tgoto cleanup;\n\t}\n\t// set default colors\n\tif (!(bar->colors.background = strndup(\"#000000ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.statusline = strndup(\"#ffffffff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.separator = strndup(\"#666666ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.focused_workspace_border = strndup(\"#4c7899ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.focused_workspace_bg = strndup(\"#285577ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.focused_workspace_text = strndup(\"#ffffffff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.active_workspace_border = strndup(\"#333333ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.active_workspace_bg = strndup(\"#5f676aff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.active_workspace_text = strndup(\"#ffffffff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.inactive_workspace_border = strndup(\"#333333ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.inactive_workspace_bg = strndup(\"#222222ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.inactive_workspace_text = strndup(\"#888888ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.urgent_workspace_border = strndup(\"#2f343aff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.urgent_workspace_bg = strndup(\"#900000ff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\tif (!(bar->colors.urgent_workspace_text = strndup(\"#ffffffff\", 9))) {\n\t\tgoto cleanup;\n\t}\n\t// if the following colors stay undefined, they fall back to background,\n\t// statusline, separator and urgent_workspace_*.\n\tbar->colors.focused_background = NULL;\n\tbar->colors.focused_statusline = NULL;\n\tbar->colors.focused_separator = NULL;\n\tbar->colors.binding_mode_border = NULL;\n\tbar->colors.binding_mode_bg = NULL;\n\tbar->colors.binding_mode_text = NULL;\n\n#if HAVE_TRAY\n\tbar->tray_padding = 2;\n\twl_list_init(&bar->tray_bindings);\n#endif\n\n\treturn bar;\ncleanup:\n\tfree_bar_config(bar);\n\treturn NULL;\n}\n\nstatic void handle_swaybar_client_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct bar_config *bar = wl_container_of(listener, bar, client_destroy);\n\twl_list_remove(&bar->client_destroy.link);\n\twl_list_init(&bar->client_destroy.link);\n\tbar->client = NULL;\n}\n\nstatic void invoke_swaybar(struct bar_config *bar) {\n\tint sockets[2];\n\tif (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"socketpair failed\");\n\t\treturn;\n\t}\n\tif (!sway_set_cloexec(sockets[0], true) || !sway_set_cloexec(sockets[1], true)) {\n\t\treturn;\n\t}\n\n\tbar->client = wl_client_create(server.wl_display, sockets[0]);\n\tif (bar->client == NULL) {\n\t\tsway_log_errno(SWAY_ERROR, \"wl_client_create failed\");\n\t\treturn;\n\t}\n\n\tbar->client_destroy.notify = handle_swaybar_client_destroy;\n\twl_client_add_destroy_listener(bar->client, &bar->client_destroy);\n\n\tpid_t pid = fork();\n\tif (pid < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create fork for swaybar\");\n\t\treturn;\n\t} else if (pid == 0) {\n\t\tif (!sway_set_cloexec(sockets[1], false)) {\n\t\t\t_exit(EXIT_FAILURE);\n\t\t}\n\n\t\tchar wayland_socket_str[16];\n\t\tsnprintf(wayland_socket_str, sizeof(wayland_socket_str),\n\t\t\t\t\"%d\", sockets[1]);\n\t\tsetenv(\"WAYLAND_SOCKET\", wayland_socket_str, true);\n\n\t\t// run custom swaybar\n\t\tchar *const cmd[] = {\n\t\t\t\tbar->swaybar_command ? bar->swaybar_command : \"swaybar\",\n\t\t\t\t\"-b\", bar->id, NULL};\n\t\texecvp(cmd[0], cmd);\n\t\t_exit(EXIT_FAILURE);\n\t}\n\n\tif (close(sockets[1]) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"close failed\");\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Spawned swaybar %s\", bar->id);\n}\n\nvoid load_swaybar(struct bar_config *bar) {\n\tif (bar->client != NULL) {\n\t\twl_client_destroy(bar->client);\n\t}\n\tsway_log(SWAY_DEBUG, \"Invoking swaybar for bar id '%s'\", bar->id);\n\tinvoke_swaybar(bar);\n}\n\nvoid load_swaybars(void) {\n\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\tstruct bar_config *bar = config->bars->items[i];\n\t\tload_swaybar(bar);\n\t}\n}\n"
  },
  {
    "path": "sway/config/input.c",
    "content": "#include <stdlib.h>\n#include <limits.h>\n#include <float.h>\n#include \"sway/config.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/server.h\"\n#include \"log.h\"\n\nstruct input_config *new_input_config(const char* identifier) {\n\tstruct input_config *input = calloc(1, sizeof(struct input_config));\n\tif (!input) {\n\t\tsway_log(SWAY_DEBUG, \"Unable to allocate input config\");\n\t\treturn NULL;\n\t}\n\tsway_log(SWAY_DEBUG, \"new_input_config(%s)\", identifier);\n\tif (!(input->identifier = strdup(identifier))) {\n\t\tfree(input);\n\t\tsway_log(SWAY_DEBUG, \"Unable to allocate input config\");\n\t\treturn NULL;\n\t}\n\n\tinput->input_type = NULL;\n\tinput->tap = INT_MIN;\n\tinput->tap_button_map = INT_MIN;\n\tinput->drag = INT_MIN;\n\tinput->drag_lock = INT_MIN;\n\tinput->dwt = INT_MIN;\n\tinput->dwtp = INT_MIN;\n\tinput->send_events = INT_MIN;\n\tinput->click_method = INT_MIN;\n\tinput->clickfinger_button_map = INT_MIN;\n\tinput->middle_emulation = INT_MIN;\n\tinput->natural_scroll = INT_MIN;\n\tinput->accel_profile = INT_MIN;\n\tinput->rotation_angle = FLT_MIN;\n\tinput->pointer_accel = FLT_MIN;\n\tinput->scroll_factor = FLT_MIN;\n\tinput->scroll_button = INT_MIN;\n\tinput->scroll_button_lock = INT_MIN;\n\tinput->scroll_method = INT_MIN;\n\tinput->left_handed = INT_MIN;\n\tinput->repeat_delay = INT_MIN;\n\tinput->repeat_rate = INT_MIN;\n\tinput->xkb_numlock = INT_MIN;\n\tinput->xkb_capslock = INT_MIN;\n\tinput->xkb_file_is_set = false;\n\tinput->tools = create_list();\n\n\treturn input;\n}\n\nvoid merge_input_config(struct input_config *dst, struct input_config *src) {\n\tif (src->accel_profile != INT_MIN) {\n\t\tdst->accel_profile = src->accel_profile;\n\t}\n\tif (src->click_method != INT_MIN) {\n\t\tdst->click_method = src->click_method;\n\t}\n\tif (src->clickfinger_button_map != INT_MIN) {\n\t\tdst->clickfinger_button_map = src->clickfinger_button_map;\n\t}\n\tif (src->drag != INT_MIN) {\n\t\tdst->drag = src->drag;\n\t}\n\tif (src->drag_lock != INT_MIN) {\n\t\tdst->drag_lock = src->drag_lock;\n\t}\n\tif (src->dwt != INT_MIN) {\n\t\tdst->dwt = src->dwt;\n\t}\n\tif (src->dwtp != INT_MIN) {\n\t\tdst->dwtp = src->dwtp;\n\t}\n\tif (src->left_handed != INT_MIN) {\n\t\tdst->left_handed = src->left_handed;\n\t}\n\tif (src->middle_emulation != INT_MIN) {\n\t\tdst->middle_emulation = src->middle_emulation;\n\t}\n\tif (src->natural_scroll != INT_MIN) {\n\t\tdst->natural_scroll = src->natural_scroll;\n\t}\n\tif (src->rotation_angle != FLT_MIN) {\n\t\tdst->rotation_angle = src->rotation_angle;\n\t}\n\tif (src->pointer_accel != FLT_MIN) {\n\t\tdst->pointer_accel = src->pointer_accel;\n\t}\n\tif (src->scroll_factor != FLT_MIN) {\n\t\tdst->scroll_factor = src->scroll_factor;\n\t}\n\tif (src->repeat_delay != INT_MIN) {\n\t\tdst->repeat_delay = src->repeat_delay;\n\t}\n\tif (src->repeat_rate != INT_MIN) {\n\t\tdst->repeat_rate = src->repeat_rate;\n\t}\n\tif (src->scroll_method != INT_MIN) {\n\t\tdst->scroll_method = src->scroll_method;\n\t}\n\tif (src->scroll_button != INT_MIN) {\n\t\tdst->scroll_button = src->scroll_button;\n\t}\n\tif (src->scroll_button_lock != INT_MIN) {\n\t\tdst->scroll_button_lock = src->scroll_button_lock;\n\t}\n\tif (src->send_events != INT_MIN) {\n\t\tdst->send_events = src->send_events;\n\t}\n\tif (src->tap != INT_MIN) {\n\t\tdst->tap = src->tap;\n\t}\n\tif (src->tap_button_map != INT_MIN) {\n\t\tdst->tap_button_map = src->tap_button_map;\n\t}\n\tif (src->xkb_file_is_set) {\n\t\tfree(dst->xkb_file);\n\t\tdst->xkb_file = src->xkb_file ? strdup(src->xkb_file) : NULL;\n\t\tdst->xkb_file_is_set = dst->xkb_file != NULL;\n\t}\n\tif (src->xkb_layout) {\n\t\tfree(dst->xkb_layout);\n\t\tdst->xkb_layout = strdup(src->xkb_layout);\n\t}\n\tif (src->xkb_model) {\n\t\tfree(dst->xkb_model);\n\t\tdst->xkb_model = strdup(src->xkb_model);\n\t}\n\tif (src->xkb_options) {\n\t\tfree(dst->xkb_options);\n\t\tdst->xkb_options = strdup(src->xkb_options);\n\t}\n\tif (src->xkb_rules) {\n\t\tfree(dst->xkb_rules);\n\t\tdst->xkb_rules = strdup(src->xkb_rules);\n\t}\n\tif (src->xkb_variant) {\n\t\tfree(dst->xkb_variant);\n\t\tdst->xkb_variant = strdup(src->xkb_variant);\n\t}\n\tif (src->xkb_numlock != INT_MIN) {\n\t\tdst->xkb_numlock = src->xkb_numlock;\n\t}\n\tif (src->xkb_capslock != INT_MIN) {\n\t\tdst->xkb_capslock = src->xkb_capslock;\n\t}\n\tif (src->mapped_from_region) {\n\t\tfree(dst->mapped_from_region);\n\t\tdst->mapped_from_region =\n\t\t\tmalloc(sizeof(struct input_config_mapped_from_region));\n\t\tmemcpy(dst->mapped_from_region, src->mapped_from_region,\n\t\t\tsizeof(struct input_config_mapped_from_region));\n\t}\n\tif (src->mapped_to) {\n\t\tdst->mapped_to = src->mapped_to;\n\t}\n\tif (src->mapped_to_output) {\n\t\tfree(dst->mapped_to_output);\n\t\tdst->mapped_to_output = strdup(src->mapped_to_output);\n\t}\n\tif (src->mapped_to_region) {\n\t\tfree(dst->mapped_to_region);\n\t\tdst->mapped_to_region =\n\t\t\tmalloc(sizeof(struct wlr_box));\n\t\tmemcpy(dst->mapped_to_region, src->mapped_to_region,\n\t\t\tsizeof(struct wlr_box));\n\t}\n\tif (src->calibration_matrix.configured) {\n\t\tdst->calibration_matrix.configured = src->calibration_matrix.configured;\n\t\tmemcpy(dst->calibration_matrix.matrix, src->calibration_matrix.matrix,\n\t\t\tsizeof(src->calibration_matrix.matrix));\n\t}\n\tfor (int i = 0; i < src->tools->length; i++) {\n\t\tstruct input_config_tool *src_tool = src->tools->items[i];\n\t\tfor (int j = 0; j < dst->tools->length; j++) {\n\t\t\tstruct input_config_tool *dst_tool = dst->tools->items[j];\n\t\t\tif (src_tool->type == dst_tool->type) {\n\t\t\t\tdst_tool->mode = src_tool->mode;\n\t\t\t\tgoto tool_merge_outer;\n\t\t\t}\n\t\t}\n\n\t\tstruct input_config_tool *dst_tool = malloc(sizeof(*dst_tool));\n\t\tmemcpy(dst_tool, src_tool, sizeof(*dst_tool));\n\t\tlist_add(dst->tools, dst_tool);\n\n\t\ttool_merge_outer:;\n\t}\n}\n\nstatic bool validate_xkb_merge(struct input_config *dest,\n\t\tstruct input_config *src, char **xkb_error) {\n\tstruct input_config *temp = new_input_config(\"temp\");\n\tif (dest) {\n\t\tmerge_input_config(temp, dest);\n\t}\n\tmerge_input_config(temp, src);\n\n\tstruct xkb_keymap *keymap = sway_keyboard_compile_keymap(temp, xkb_error);\n\tfree_input_config(temp);\n\tif (!keymap) {\n\t\treturn false;\n\t}\n\n\txkb_keymap_unref(keymap);\n\treturn true;\n}\n\nstatic bool validate_wildcard_on_all(struct input_config *wildcard,\n\t\tchar **error) {\n\tfor (int i = 0; i < config->input_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_configs->items[i];\n\t\tif (strcmp(wildcard->identifier, ic->identifier) != 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Validating xkb merge of * on %s\",\n\t\t\t\t\tic->identifier);\n\t\t\tif (!validate_xkb_merge(ic, wildcard, error)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (int i = 0; i < config->input_type_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_type_configs->items[i];\n\t\tsway_log(SWAY_DEBUG, \"Validating xkb merge of * config on %s\",\n\t\t\t\tic->identifier);\n\t\tif (!validate_xkb_merge(ic, wildcard, error)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic void merge_wildcard_on_all(struct input_config *wildcard) {\n\tfor (int i = 0; i < config->input_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_configs->items[i];\n\t\tif (strcmp(wildcard->identifier, ic->identifier) != 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Merging input * config on %s\", ic->identifier);\n\t\t\tmerge_input_config(ic, wildcard);\n\t\t}\n\t}\n\n\tfor (int i = 0; i < config->input_type_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_type_configs->items[i];\n\t\tsway_log(SWAY_DEBUG, \"Merging input * config on %s\", ic->identifier);\n\t\tmerge_input_config(ic, wildcard);\n\t}\n}\n\nstatic bool validate_type_on_existing(struct input_config *type_wildcard,\n\t\tchar **error) {\n\tfor (int i = 0; i < config->input_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_configs->items[i];\n\t\tif (ic->input_type == NULL) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Validating merge of %s on %s\",\n\t\t\t\ttype_wildcard->identifier, ic->identifier);\n\t\t\tif (!validate_xkb_merge(ic, type_wildcard, error)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic void merge_type_on_existing(struct input_config *type_wildcard) {\n\tfor (int i = 0; i < config->input_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_configs->items[i];\n\t\tif (ic->input_type == NULL) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Merging %s top of %s\",\n\t\t\t\ttype_wildcard->identifier,\n\t\t\t\tic->identifier);\n\t\t\tmerge_input_config(ic, type_wildcard);\n\t\t}\n\t}\n}\n\nstatic const char *set_input_type(struct input_config *ic) {\n\tstruct sway_input_device *input_device;\n\twl_list_for_each(input_device, &server.input->devices, link) {\n\t\tif (strcmp(input_device->identifier, ic->identifier) == 0) {\n\t\t\tic->input_type = input_device_get_type(input_device);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn ic->input_type;\n}\n\nstruct input_config *store_input_config(struct input_config *ic,\n\t\tchar **error) {\n\tbool wildcard = strcmp(ic->identifier, \"*\") == 0;\n\tif (wildcard && error && !validate_wildcard_on_all(ic, error)) {\n\t\treturn NULL;\n\t}\n\n\tbool type = has_prefix(ic->identifier, \"type:\");\n\tif (type && error && !validate_type_on_existing(ic, error)) {\n\t\treturn NULL;\n\t}\n\n\tlist_t *config_list = type ? config->input_type_configs\n\t\t: config->input_configs;\n\n\tstruct input_config *current = NULL;\n\tbool new_current = false;\n\n\tint i = list_seq_find(config_list, input_identifier_cmp, ic->identifier);\n\tif (i >= 0) {\n\t\tcurrent = config_list->items[i];\n\t}\n\n\tif (!current && !wildcard && !type && set_input_type(ic)) {\n\t\tfor (i = 0; i < config->input_type_configs->length; i++) {\n\t\t\tstruct input_config *tc = config->input_type_configs->items[i];\n\t\t\tif (strcmp(ic->input_type, tc->identifier + 5) == 0) {\n\t\t\t\tcurrent = new_input_config(ic->identifier);\n\t\t\t\tcurrent->input_type = ic->input_type;\n\t\t\t\tmerge_input_config(current, tc);\n\t\t\t\tnew_current = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\ti = list_seq_find(config->input_configs, input_identifier_cmp, \"*\");\n\tif (!current && i >= 0) {\n\t\tcurrent = new_input_config(ic->identifier);\n\t\tmerge_input_config(current, config->input_configs->items[i]);\n\t\tnew_current = true;\n\t}\n\n\tif (error && !validate_xkb_merge(current, ic, error)) {\n\t\tif (new_current) {\n\t\t\tfree_input_config(current);\n\t\t}\n\t\treturn NULL;\n\t}\n\n\tif (wildcard) {\n\t\tmerge_wildcard_on_all(ic);\n\t}\n\n\tif (type) {\n\t\tmerge_type_on_existing(ic);\n\t}\n\n\tif (current) {\n\t\tmerge_input_config(current, ic);\n\t\tfree_input_config(ic);\n\t\tic = current;\n\t}\n\n\tic->xkb_file_is_set = ic->xkb_file != NULL;\n\n\tif (!current || new_current) {\n\t\tlist_add(config_list, ic);\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Config stored for input %s\", ic->identifier);\n\n\treturn ic;\n}\n\nvoid input_config_fill_rule_names(struct input_config *ic,\n\t\tstruct xkb_rule_names *rules) {\n\trules->layout = ic->xkb_layout;\n\trules->model = ic->xkb_model;\n\trules->options = ic->xkb_options;\n\trules->rules = ic->xkb_rules;\n\trules->variant = ic->xkb_variant;\n}\n\nvoid free_input_config(struct input_config *ic) {\n\tif (!ic) {\n\t\treturn;\n\t}\n\tfree(ic->identifier);\n\tfree(ic->xkb_file);\n\tfree(ic->xkb_layout);\n\tfree(ic->xkb_model);\n\tfree(ic->xkb_options);\n\tfree(ic->xkb_rules);\n\tfree(ic->xkb_variant);\n\tfree(ic->mapped_from_region);\n\tfree(ic->mapped_to_output);\n\tfree(ic->mapped_to_region);\n\tlist_free_items_and_destroy(ic->tools);\n\tfree(ic);\n}\n\nint input_identifier_cmp(const void *item, const void *data) {\n\tconst struct input_config *ic = item;\n\tconst char *identifier = data;\n\treturn strcmp(ic->identifier, identifier);\n}\n"
  },
  {
    "path": "sway/config/output.c",
    "content": "#include <assert.h>\n#include <drm_fourcc.h>\n#include <stdbool.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <wlr/config.h>\n#include <wlr/render/allocator.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_output_swapchain_manager.h>\n#include <xf86drm.h>\n#include \"sway/config.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/layers.h\"\n#include \"sway/lock.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/root.h\"\n#include \"log.h\"\n#include \"util.h\"\n\n#if WLR_HAS_DRM_BACKEND\n#include <wlr/backend/drm.h>\n#endif\n\nvoid output_get_identifier(char *identifier, size_t len,\n\t\tstruct sway_output *output) {\n\tstruct wlr_output *wlr_output = output->wlr_output;\n\tsnprintf(identifier, len, \"%s %s %s\",\n\t\twlr_output->make ? wlr_output->make : \"Unknown\",\n\t\twlr_output->model ? wlr_output->model : \"Unknown\",\n\t\twlr_output->serial ? wlr_output->serial : \"Unknown\");\n}\n\nconst char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {\n\tswitch (scale_filter) {\n\tcase SCALE_FILTER_DEFAULT:\n\t\treturn \"smart\";\n\tcase SCALE_FILTER_LINEAR:\n\t\treturn \"linear\";\n\tcase SCALE_FILTER_NEAREST:\n\t\treturn \"nearest\";\n\tcase SCALE_FILTER_SMART:\n\t\treturn \"smart\";\n\t}\n\tsway_assert(false, \"Unknown value for scale_filter.\");\n\treturn NULL;\n}\n\nstruct output_config *new_output_config(const char *name) {\n\tstruct output_config *oc = calloc(1, sizeof(struct output_config));\n\tif (oc == NULL) {\n\t\treturn NULL;\n\t}\n\toc->name = strdup(name);\n\tif (oc->name == NULL) {\n\t\tfree(oc);\n\t\treturn NULL;\n\t}\n\toc->enabled = -1;\n\toc->width = oc->height = -1;\n\toc->refresh_rate = -1;\n\toc->custom_mode = -1;\n\toc->drm_mode.type = -1;\n\toc->x = oc->y = INT_MAX;\n\toc->scale = -1;\n\toc->scale_filter = SCALE_FILTER_DEFAULT;\n\toc->transform = -1;\n\toc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;\n\toc->max_render_time = -1;\n\toc->adaptive_sync = -1;\n\toc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;\n\toc->color_profile = COLOR_PROFILE_DEFAULT;\n\toc->color_transform = NULL;\n\toc->power = -1;\n\toc->allow_tearing = -1;\n\toc->hdr = -1;\n\treturn oc;\n}\n\n// supersede_output_config clears all fields in dst that were set in src\nstatic void supersede_output_config(struct output_config *dst, struct output_config *src) {\n\tif (src->enabled != -1) {\n\t\tdst->enabled = -1;\n\t}\n\tif (src->width != -1) {\n\t\tdst->width = -1;\n\t}\n\tif (src->height != -1) {\n\t\tdst->height = -1;\n\t}\n\tif (src->x != INT_MAX) {\n\t\tdst->x = INT_MAX;\n\t}\n\tif (src->y != INT_MAX) {\n\t\tdst->y = INT_MAX;\n\t}\n\tif (src->scale != -1) {\n\t\tdst->scale = -1;\n\t}\n\tif (src->scale_filter != SCALE_FILTER_DEFAULT) {\n\t\tdst->scale_filter = SCALE_FILTER_DEFAULT;\n\t}\n\tif (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {\n\t\tdst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;\n\t}\n\tif (src->refresh_rate != -1) {\n\t\tdst->refresh_rate = -1;\n\t}\n\tif (src->custom_mode != -1) {\n\t\tdst->custom_mode = -1;\n\t}\n\tif (src->drm_mode.type != (uint32_t) -1) {\n\t\tdst->drm_mode.type = -1;\n\t}\n\tif (src->transform != -1) {\n\t\tdst->transform = -1;\n\t}\n\tif (src->max_render_time != -1) {\n\t\tdst->max_render_time = -1;\n\t}\n\tif (src->adaptive_sync != -1) {\n\t\tdst->adaptive_sync = -1;\n\t}\n\tif (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {\n\t\tdst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;\n\t}\n\tif (src->color_profile != COLOR_PROFILE_DEFAULT) {\n\t\tif (dst->color_transform) {\n\t\t\twlr_color_transform_unref(dst->color_transform);\n\t\t\tdst->color_transform = NULL;\n\t\t}\n\t\tdst->color_profile = COLOR_PROFILE_DEFAULT;\n\t}\n\tif (src->background) {\n\t\tfree(dst->background);\n\t\tdst->background = NULL;\n\t}\n\tif (src->background_option) {\n\t\tfree(dst->background_option);\n\t\tdst->background_option = NULL;\n\t}\n\tif (src->background_fallback) {\n\t\tfree(dst->background_fallback);\n\t\tdst->background_fallback = NULL;\n\t}\n\tif (src->power != -1) {\n\t\tdst->power = -1;\n\t}\n\tif (src->allow_tearing != -1) {\n\t\tdst->allow_tearing = -1;\n\t}\n\tif (src->hdr != -1) {\n\t\tdst->hdr = -1;\n\t}\n}\n\n// merge_output_config sets all fields in dst that were set in src\nstatic void merge_output_config(struct output_config *dst, struct output_config *src) {\n\tif (src->enabled != -1) {\n\t\tdst->enabled = src->enabled;\n\t}\n\tif (src->width != -1) {\n\t\tdst->width = src->width;\n\t}\n\tif (src->height != -1) {\n\t\tdst->height = src->height;\n\t}\n\tif (src->x != INT_MAX) {\n\t\tdst->x = src->x;\n\t}\n\tif (src->y != INT_MAX) {\n\t\tdst->y = src->y;\n\t}\n\tif (src->scale != -1) {\n\t\tdst->scale = src->scale;\n\t}\n\tif (src->scale_filter != SCALE_FILTER_DEFAULT) {\n\t\tdst->scale_filter = src->scale_filter;\n\t}\n\tif (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {\n\t\tdst->subpixel = src->subpixel;\n\t}\n\tif (src->refresh_rate != -1) {\n\t\tdst->refresh_rate = src->refresh_rate;\n\t}\n\tif (src->custom_mode != -1) {\n\t\tdst->custom_mode = src->custom_mode;\n\t}\n\tif (src->drm_mode.type != (uint32_t) -1) {\n\t\tmemcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));\n\t}\n\tif (src->transform != -1) {\n\t\tdst->transform = src->transform;\n\t}\n\tif (src->max_render_time != -1) {\n\t\tdst->max_render_time = src->max_render_time;\n\t}\n\tif (src->adaptive_sync != -1) {\n\t\tdst->adaptive_sync = src->adaptive_sync;\n\t}\n\tif (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {\n\t\tdst->render_bit_depth = src->render_bit_depth;\n\t}\n\tif (src->color_profile != COLOR_PROFILE_DEFAULT) {\n\t\tif (src->color_transform) {\n\t\t\twlr_color_transform_ref(src->color_transform);\n\t\t}\n\t\twlr_color_transform_unref(dst->color_transform);\n\t\tdst->color_profile = src->color_profile;\n\t\tdst->color_transform = src->color_transform;\n\t}\n\tif (src->background) {\n\t\tfree(dst->background);\n\t\tdst->background = strdup(src->background);\n\t}\n\tif (src->background_option) {\n\t\tfree(dst->background_option);\n\t\tdst->background_option = strdup(src->background_option);\n\t}\n\tif (src->background_fallback) {\n\t\tfree(dst->background_fallback);\n\t\tdst->background_fallback = strdup(src->background_fallback);\n\t}\n\tif (src->power != -1) {\n\t\tdst->power = src->power;\n\t}\n\tif (src->allow_tearing != -1) {\n\t\tdst->allow_tearing = src->allow_tearing;\n\t}\n\tif (src->hdr != -1) {\n\t\tdst->hdr = src->hdr;\n\t}\n}\n\nvoid store_output_config(struct output_config *oc) {\n\tbool merged = false;\n\tbool wildcard = strcmp(oc->name, \"*\") == 0;\n\tstruct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);\n\n\tchar id[128];\n\tif (output) {\n\t\toutput_get_identifier(id, sizeof(id), output);\n\t}\n\n\tfor (int i = 0; i < config->output_configs->length; i++) {\n\t\tstruct output_config *old = config->output_configs->items[i];\n\n\t\t// If the old config matches the new config's name, regardless of\n\t\t// whether it was name or identifier, merge on top of the existing\n\t\t// config. If the new config is a wildcard, this also merges on top of\n\t\t// old wildcard configs.\n\t\tif (strcmp(old->name, oc->name) == 0) {\n\t\t\tmerge_output_config(old, oc);\n\t\t\tmerged = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If the new config is a wildcard config we supersede all non-wildcard\n\t\t// configs. Old wildcard configs have already been handled above.\n\t\tif (wildcard) {\n\t\t\tsupersede_output_config(old, oc);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If the new config matches an output's name, and the old config\n\t\t// matches on that output's identifier, supersede it.\n\t\tif (output && strcmp(old->name, id) == 0 &&\n\t\t\t\tstrcmp(oc->name, output->wlr_output->name) == 0) {\n\t\t\tsupersede_output_config(old, oc);\n\t\t}\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Config stored for output %s (enabled: %d) (%dx%d@%fHz \"\n\t\t\"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) \"\n\t\t\"(max render time: %d) (allow tearing: %d) (hdr: %d)\",\n\t\toc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,\n\t\toc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),\n\t\toc->transform, oc->background, oc->background_option, oc->power,\n\t\toc->max_render_time, oc->allow_tearing, oc->hdr);\n\n\t// If the configuration was not merged into an existing configuration, add\n\t// it to the list. Otherwise we're done with it and can free it.\n\tif (!merged) {\n\t\tlist_add(config->output_configs, oc);\n\t} else {\n\t\tfree_output_config(oc);\n\t}\n}\n\nstatic void set_mode(struct wlr_output *output, struct wlr_output_state *pending,\n\t\tint width, int height, float refresh_rate, bool custom) {\n\t// Not all floating point integers can be represented exactly\n\t// as (int)(1000 * mHz / 1000.f)\n\t// round() the result to avoid any error\n\tint mhz = (int)roundf(refresh_rate * 1000);\n\t// If no target refresh rate is given, match highest available\n\tmhz = mhz <= 0 ? INT_MAX : mhz;\n\n\tif (wl_list_empty(&output->modes) || custom) {\n\t\twlr_output_state_set_custom_mode(pending, width, height,\n\t\t\trefresh_rate > 0 ? mhz : 0);\n\t\treturn;\n\t}\n\n\tstruct wlr_output_mode *mode, *best = NULL;\n\tint best_diff_mhz = INT_MAX;\n\twl_list_for_each(mode, &output->modes, link) {\n\t\tif (mode->width == width && mode->height == height) {\n\t\t\tint diff_mhz = abs(mode->refresh - mhz);\n\t\t\tif (diff_mhz < best_diff_mhz) {\n\t\t\t\tbest_diff_mhz = diff_mhz;\n\t\t\t\tbest = mode;\n\t\t\t\tif (best_diff_mhz == 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (!best) {\n\t\tbest = wlr_output_preferred_mode(output);\n\t\tsway_log(SWAY_INFO, \"Configured mode (%dx%d@%.3fHz) not available, \"\n\t\t\t\"applying preferred mode (%dx%d@%.3fHz)\",\n\t\t\twidth, height, refresh_rate,\n\t\t\tbest->width, best->height, best->refresh / 1000.f);\n\t}\n\twlr_output_state_set_mode(pending, best);\n}\n\nstatic void set_modeline(struct wlr_output *output,\n\t\tstruct wlr_output_state *pending, drmModeModeInfo *drm_mode) {\n#if WLR_HAS_DRM_BACKEND\n\tif (!wlr_output_is_drm(output)) {\n\t\tsway_log(SWAY_ERROR, \"Modeline can only be set to DRM output\");\n\t\treturn;\n\t}\n\tstruct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);\n\tif (mode) {\n\t\twlr_output_state_set_mode(pending, mode);\n\t}\n#else\n\tsway_log(SWAY_ERROR, \"Modeline can only be set to DRM output\");\n#endif\n}\n\nbool output_supports_hdr(struct wlr_output *output, const char **unsupported_reason_ptr) {\n\tconst char *unsupported_reason = NULL;\n\tif (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020)) {\n\t\tunsupported_reason = \"BT2020 primaries not supported by output\";\n\t} else if (!(output->supported_transfer_functions & WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) {\n\t\tunsupported_reason = \"PQ transfer function not supported by output\";\n\t} else if (!server.renderer->features.output_color_transform) {\n\t\tunsupported_reason = \"renderer doesn't support output color transforms\";\n\t}\n\tif (unsupported_reason_ptr != NULL) {\n\t\t*unsupported_reason_ptr = unsupported_reason;\n\t}\n\treturn unsupported_reason == NULL;\n}\n\nstatic void set_hdr(struct wlr_output *output, struct wlr_output_state *pending, bool enabled) {\n\tconst char *unsupported_reason = NULL;\n\tif (enabled && !output_supports_hdr(output, &unsupported_reason)) {\n\t\tsway_log(SWAY_ERROR, \"Cannot enable HDR on output %s: %s\",\n\t\t\toutput->name, unsupported_reason);\n\t\tenabled = false;\n\t}\n\n\tif (!enabled) {\n\t\tif (output->supported_primaries != 0 || output->supported_transfer_functions != 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Disabling HDR on output %s\", output->name);\n\t\t\twlr_output_state_set_image_description(pending, NULL);\n\t\t}\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Enabling HDR on output %s\", output->name);\n\tconst struct wlr_output_image_description image_desc = {\n\t\t.primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020,\n\t\t.transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ,\n\t};\n\twlr_output_state_set_image_description(pending, &image_desc);\n}\n\n/* Some manufacturers hardcode the aspect-ratio of the output in the physical\n * size field. */\nstatic bool phys_size_is_aspect_ratio(struct wlr_output *output) {\n\treturn (output->phys_width == 1600 && output->phys_height == 900) ||\n\t\t(output->phys_width == 1600 && output->phys_height == 1000) ||\n\t\t(output->phys_width == 160 && output->phys_height == 90) ||\n\t\t(output->phys_width == 160 && output->phys_height == 100) ||\n\t\t(output->phys_width == 16 && output->phys_height == 9) ||\n\t\t(output->phys_width == 16 && output->phys_height == 10);\n}\n\n// The minimum DPI at which we turn on a scale of 2\n#define HIDPI_DPI_LIMIT (2 * 96)\n// The minimum screen height at which we turn on a scale of 2\n#define HIDPI_MIN_HEIGHT 1200\n// 1 inch = 25.4 mm\n#define MM_PER_INCH 25.4\n\nstatic int compute_default_scale(struct wlr_output *output,\n\t\tstruct wlr_output_state *pending) {\n\tstruct wlr_box box = { .width = output->width, .height = output->height };\n\tif (pending->committed & WLR_OUTPUT_STATE_MODE) {\n\t\tswitch (pending->mode_type) {\n\t\tcase WLR_OUTPUT_STATE_MODE_FIXED:\n\t\t\tbox.width = pending->mode->width;\n\t\t\tbox.height = pending->mode->height;\n\t\t\tbreak;\n\t\tcase WLR_OUTPUT_STATE_MODE_CUSTOM:\n\t\t\tbox.width = pending->custom_mode.width;\n\t\t\tbox.height = pending->custom_mode.height;\n\t\t\tbreak;\n\t\t}\n\t}\n\tenum wl_output_transform transform = output->transform;\n\tif (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {\n\t\ttransform = pending->transform;\n\t}\n\twlr_box_transform(&box, &box, transform, box.width, box.height);\n\n\tint width = box.width;\n\tint height = box.height;\n\n\tif (height < HIDPI_MIN_HEIGHT) {\n\t\treturn 1;\n\t}\n\n\tif (output->phys_width == 0 || output->phys_height == 0) {\n\t\treturn 1;\n\t}\n\n\tif (phys_size_is_aspect_ratio(output)) {\n\t\treturn 1;\n\t}\n\n\tdouble dpi_x = (double) width / (output->phys_width / MM_PER_INCH);\n\tdouble dpi_y = (double) height / (output->phys_height / MM_PER_INCH);\n\tif (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) {\n\t\treturn 1;\n\t}\n\n\treturn 2;\n}\n\nstatic enum render_bit_depth bit_depth_from_format(uint32_t render_format) {\n\tif (render_format == DRM_FORMAT_XRGB2101010 || render_format == DRM_FORMAT_XBGR2101010) {\n\t\treturn RENDER_BIT_DEPTH_10;\n\t} else if (render_format == DRM_FORMAT_XRGB8888 || render_format == DRM_FORMAT_ARGB8888) {\n\t\treturn RENDER_BIT_DEPTH_8;\n\t} else if (render_format == DRM_FORMAT_RGB565) {\n\t\treturn RENDER_BIT_DEPTH_6;\n\t}\n\treturn RENDER_BIT_DEPTH_DEFAULT;\n}\n\nstatic enum render_bit_depth get_config_render_bit_depth(const struct output_config *oc) {\n\tif (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {\n\t\treturn oc->render_bit_depth;\n\t}\n\tif (oc && oc->hdr == 1) {\n\t\treturn RENDER_BIT_DEPTH_10;\n\t}\n\treturn RENDER_BIT_DEPTH_8;\n}\n\nstatic bool render_format_is_bgr(uint32_t fmt) {\n\treturn fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;\n}\n\nstatic bool output_config_is_disabling(struct output_config *oc) {\n\treturn oc && (!oc->enabled || oc->power == 0);\n}\n\nstatic void queue_output_config(struct output_config *oc,\n\t\tstruct sway_output *output, struct wlr_output_state *pending) {\n\tif (output == root->fallback_output) {\n\t\treturn;\n\t}\n\n\tstruct wlr_output *wlr_output = output->wlr_output;\n\n\tif (output_config_is_disabling(oc)) {\n\t\twlr_output_state_set_enabled(pending, false);\n\t\treturn;\n\t}\n\twlr_output_state_set_enabled(pending, true);\n\n\tif (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {\n\t\tset_modeline(wlr_output, pending, &oc->drm_mode);\n\t} else if (oc && oc->width > 0 && oc->height > 0) {\n\t\tset_mode(wlr_output, pending, oc->width, oc->height,\n\t\t\toc->refresh_rate, oc->custom_mode == 1);\n\t} else if (!wl_list_empty(&wlr_output->modes)) {\n\t\tstruct wlr_output_mode *preferred_mode =\n\t\t\twlr_output_preferred_mode(wlr_output);\n\t\twlr_output_state_set_mode(pending, preferred_mode);\n\t}\n\n\tif (oc && oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {\n\t\twlr_output_state_set_subpixel(pending, oc->subpixel);\n\t} else {\n\t\twlr_output_state_set_subpixel(pending, output->detected_subpixel);\n\t}\n\n\tif (oc && oc->transform >= 0) {\n\t\twlr_output_state_set_transform(pending, oc->transform);\n#if WLR_HAS_DRM_BACKEND\n\t} else if (wlr_output_is_drm(wlr_output)) {\n\t\twlr_output_state_set_transform(pending,\n\t\t\twlr_drm_connector_get_panel_orientation(wlr_output));\n#endif\n\t} else {\n\t\twlr_output_state_set_transform(pending, WL_OUTPUT_TRANSFORM_NORMAL);\n\t}\n\n\t// Apply the scale after sorting out the mode, because the scale\n\t// auto-detection reads the pending output size\n\tif (oc && oc->scale > 0) {\n\t\t// The factional-scale-v1 protocol uses increments of 120ths to send\n\t\t// the scale factor to the client. Adjust the scale so that we use the\n\t\t// same value as the clients'.\n\t\twlr_output_state_set_scale(pending, round(oc->scale * 120) / 120);\n\t} else {\n\t\twlr_output_state_set_scale(pending,\n\t\t\tcompute_default_scale(wlr_output, pending));\n\t}\n\n\tif (wlr_output->adaptive_sync_supported) {\n\t\tif (oc && oc->adaptive_sync != -1) {\n\t\t\twlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);\n\t\t} else {\n\t\t\twlr_output_state_set_adaptive_sync_enabled(pending, false);\n\t\t}\n\t}\n\n\tenum render_bit_depth render_bit_depth = get_config_render_bit_depth(oc);\n\tif (render_bit_depth == RENDER_BIT_DEPTH_10 &&\n\t\t\tbit_depth_from_format(output->wlr_output->render_format) == render_bit_depth) {\n\t\t// 10-bit was set successfully before, try to save some tests by reusing the format\n\t\twlr_output_state_set_render_format(pending, output->wlr_output->render_format);\n\t} else if (render_bit_depth == RENDER_BIT_DEPTH_10) {\n\t\twlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);\n\t} else if (render_bit_depth == RENDER_BIT_DEPTH_6) {\n\t\twlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565);\n\t} else {\n\t\twlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);\n\t}\n\n\tbool hdr = oc && oc->hdr == 1;\n\tbool color_profile = oc && (oc->color_transform != NULL\n\t\t|| oc->color_profile == COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES);\n\tif (hdr && color_profile) {\n\t\tsway_log(SWAY_ERROR, \"Cannot use HDR on output %s: output has a color profile set\", wlr_output->name);\n\t\thdr = false;\n\t}\n\tset_hdr(wlr_output, pending, hdr);\n}\n\nstruct config_output_state {\n\tstruct wlr_color_transform *color_transform;\n};\n\nstatic void config_output_state_finish(struct config_output_state *state) {\n\twlr_color_transform_unref(state->color_transform);\n}\n\nstatic struct wlr_color_transform *color_profile_from_device(struct wlr_output *wlr_output,\n\t\tstruct wlr_color_transform *transfer_function) {\n\tstruct wlr_color_primaries srgb_primaries;\n\twlr_color_primaries_from_named(&srgb_primaries, WLR_COLOR_NAMED_PRIMARIES_SRGB);\n\n\tconst struct wlr_color_primaries *primaries = wlr_output->default_primaries;\n\tif (primaries == NULL) {\n\t\tsway_log(SWAY_INFO, \"output has no reported color information\");\n\t\tif (transfer_function) {\n\t\t\twlr_color_transform_ref(transfer_function);\n\t\t}\n\t\treturn transfer_function;\n\t} else if (memcmp(primaries, &srgb_primaries, sizeof(*primaries)) == 0) {\n\t\tsway_log(SWAY_INFO, \"output reports sRGB colors, no correction needed\");\n\t\tif (transfer_function) {\n\t\t\twlr_color_transform_ref(transfer_function);\n\t\t}\n\t\treturn transfer_function;\n\t} else {\n\t\tsway_log(SWAY_INFO, \"Creating color profile from reported color primaries: \"\n\t\t\t\t\"R(%f, %f) G(%f, %f) B(%f, %f) W(%f, %f)\",\n\t\t\tprimaries->red.x, primaries->red.y, primaries->green.x, primaries->green.y,\n\t\t\tprimaries->blue.x, primaries->blue.y, primaries->white.x, primaries->white.y);\n\t\tfloat matrix[9];\n\t\twlr_color_primaries_transform_absolute_colorimetric(&srgb_primaries, primaries, matrix);\n\t\tstruct wlr_color_transform *matrix_transform = wlr_color_transform_init_matrix(matrix);\n\t\tif (matrix_transform == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t\tstruct wlr_color_transform *resolved_tf = transfer_function ?\n\t\t\twlr_color_transform_ref(transfer_function) :\n\t\t\twlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_GAMMA22);\n\t\tif (resolved_tf == NULL) {\n\t\t\twlr_color_transform_unref(matrix_transform);\n\t\t\treturn NULL;\n\t\t}\n\t\tstruct wlr_color_transform *transforms[] = { matrix_transform, resolved_tf };\n\t\tsize_t transforms_len = sizeof(transforms) / sizeof(transforms[0]);\n\t\tstruct wlr_color_transform *result = wlr_color_transform_init_pipeline(transforms, transforms_len);\n\t\twlr_color_transform_unref(matrix_transform);\n\t\twlr_color_transform_unref(resolved_tf);\n\t\treturn result;\n\t}\n}\n\nstatic struct wlr_color_transform *get_color_profile(struct wlr_output *output,\n\t\tstruct output_config *oc) {\n\tif (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM) {\n\t\tif (oc->color_transform) {\n\t\t\twlr_color_transform_ref(oc->color_transform);\n\t\t}\n\t\treturn oc->color_transform;\n\t} else if (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES) {\n\t\treturn color_profile_from_device(output, oc->color_transform);\n\t} else {\n\t\treturn NULL;\n\t}\n}\n\nstatic bool finalize_output_config(struct output_config *oc, struct sway_output *output,\n\t\tconst struct wlr_output_state *applied, const struct config_output_state *config_applied) {\n\tif (output == root->fallback_output) {\n\t\treturn false;\n\t}\n\n\tstruct wlr_output *wlr_output = output->wlr_output;\n\tif (oc && !oc->enabled) {\n\t\tsway_log(SWAY_DEBUG, \"Disabling output %s\", oc->name);\n\t\tif (output->enabled) {\n\t\t\toutput_disable(output);\n\t\t\twlr_output_layout_remove(root->output_layout, wlr_output);\n\t\t}\n\t\treturn true;\n\t}\n\n\tenum scale_filter_mode scale_filter_old = output->scale_filter;\n\tenum scale_filter_mode scale_filter_new = oc ? oc->scale_filter : SCALE_FILTER_DEFAULT;\n\tswitch (scale_filter_new) {\n\t\tcase SCALE_FILTER_DEFAULT:\n\t\tcase SCALE_FILTER_SMART:\n\t\t\toutput->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ?\n\t\t\t\tSCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR;\n\t\t\tbreak;\n\t\tcase SCALE_FILTER_LINEAR:\n\t\tcase SCALE_FILTER_NEAREST:\n\t\t\toutput->scale_filter = scale_filter_new;\n\t\t\tbreak;\n\t}\n\tif (scale_filter_old != output->scale_filter) {\n\t\tsway_log(SWAY_DEBUG, \"Set %s scale_filter to %s\", oc->name,\n\t\t\tsway_output_scale_filter_to_string(output->scale_filter));\n\t\twlr_damage_ring_add_whole(&output->scene_output->damage_ring);\n\t}\n\n\t// Find position for it\n\tif (oc && oc->x != INT_MAX && oc->y != INT_MAX) {\n\t\tsway_log(SWAY_DEBUG, \"Set %s position to %d, %d\", oc->name, oc->x, oc->y);\n\t\twlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y);\n\t} else {\n\t\twlr_output_layout_add_auto(root->output_layout, wlr_output);\n\t}\n\n\tif (!output->enabled) {\n\t\toutput_enable(output);\n\t}\n\n\twlr_color_transform_unref(output->color_transform);\n\tif (config_applied->color_transform != NULL) {\n\t\twlr_color_transform_ref(config_applied->color_transform);\n\t}\n\toutput->color_transform = config_applied->color_transform;\n\n\toutput->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0;\n\toutput->allow_tearing = oc && oc->allow_tearing > 0;\n\toutput->hdr = applied->image_description != NULL;\n\n\treturn true;\n}\n\nstatic void output_update_position(struct sway_output *output) {\n\tstruct wlr_box output_box;\n\twlr_output_layout_get_box(root->output_layout, output->wlr_output, &output_box);\n\toutput->lx = output_box.x;\n\toutput->ly = output_box.y;\n\toutput->width = output_box.width;\n\toutput->height = output_box.height;\n}\n\n// find_output_config_from_list returns a merged output_config containing all\n// stored configuration that applies to the specified output.\nstatic struct output_config *find_output_config_from_list(\n\t\tstruct output_config **configs, size_t configs_len,\n\t\tstruct sway_output *sway_output) {\n\tconst char *name = sway_output->wlr_output->name;\n\tstruct output_config *result = new_output_config(name);\n\tif (result == NULL) {\n\t\treturn NULL;\n\t}\n\n\tchar id[128];\n\toutput_get_identifier(id, sizeof(id), sway_output);\n\n\t// We take a new config and merge on top, in order, the wildcard config,\n\t// output config by name, and output config by identifier to form the final\n\t// config. If there are multiple matches, they are merged in order.\n\tstruct output_config *oc = NULL;\n\tconst char *names[] = {\"*\", name, id, NULL};\n\tfor (const char **name = &names[0]; *name; name++) {\n\t\tfor (size_t idx = 0; idx < configs_len; idx++) {\n\t\t\toc = configs[idx];\n\t\t\tif (strcmp(oc->name, *name) == 0) {\n\t\t\t\tmerge_output_config(result, oc);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\nstruct output_config *find_output_config(struct sway_output *sway_output) {\n\treturn find_output_config_from_list(\n\t\t\t(struct output_config **)config->output_configs->items,\n\t\t\tconfig->output_configs->length, sway_output);\n}\n\nstatic bool config_has_manual_mode(struct output_config *oc) {\n\tif (!oc) {\n\t\treturn false;\n\t}\n\tif (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {\n\t\treturn true;\n\t} else if (oc->width > 0 && oc->height > 0) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * An output config pre-matched to an output\n */\nstruct matched_output_config {\n\tstruct sway_output *output;\n\tstruct output_config *config;\n};\n\nstruct search_context {\n\tstruct wlr_output_swapchain_manager *swapchain_mgr;\n\tstruct wlr_backend_output_state *states;\n\tstruct matched_output_config *configs;\n\tsize_t configs_len;\n\tbool degrade_to_off;\n};\n\nstatic void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {\n\tsway_log(SWAY_DEBUG, \"Output state for %s\", wlr_output->name);\n\tif (state->committed & WLR_OUTPUT_STATE_ENABLED) {\n\t\tsway_log(SWAY_DEBUG, \"    enabled:       %s\", state->enabled ? \"yes\" : \"no\");\n\t}\n\tif (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {\n\t\tchar *format_name = drmGetFormatName(state->render_format);\n\t\tsway_log(SWAY_DEBUG, \"    render_format: %s\", format_name);\n\t\tfree(format_name);\n\t}\n\tif (state->committed & WLR_OUTPUT_STATE_MODE) {\n\t\tif (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {\n\t\t\tsway_log(SWAY_DEBUG, \"    custom mode:   %dx%d@%dmHz\",\n\t\t\t\tstate->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);\n\t\t} else {\n\t\t\tsway_log(SWAY_DEBUG, \"    mode:          %dx%d@%dmHz%s\",\n\t\t\t\tstate->mode->width, state->mode->height, state->mode->refresh,\n\t\t\t\tstate->mode->preferred ? \" (preferred)\" : \"\");\n\t\t}\n\t}\n\tif (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {\n\t\tsway_log(SWAY_DEBUG, \"    adaptive_sync: %s\",\n\t\t\tstate->adaptive_sync_enabled ? \"enabled\": \"disabled\");\n\t}\n\tif (state->committed & WLR_OUTPUT_STATE_SCALE) {\n\t\tsway_log(SWAY_DEBUG, \"    scale:         %f\", state->scale);\n\t}\n\tif (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) {\n\t\tsway_log(SWAY_DEBUG, \"    subpixel:      %s\",\n\t\t\tsway_wl_output_subpixel_to_string(state->subpixel));\n\t}\n}\n\nstatic bool search_valid_config(struct search_context *ctx, size_t output_idx);\n\nstatic void reset_output_state(struct wlr_output_state *state) {\n\twlr_output_state_finish(state);\n\twlr_output_state_init(state);\n\tstate->committed = 0;\n}\n\nstatic void clear_later_output_states(struct wlr_backend_output_state *states,\n\t\tsize_t configs_len, size_t output_idx) {\n\n\t// Clear and disable all output states after this one to avoid conflict\n\t// with previous tests.\n\tfor (size_t idx = output_idx+1; idx < configs_len; idx++) {\n\t\tstruct wlr_backend_output_state *backend_state = &states[idx];\n\t\tstruct wlr_output_state *state = &backend_state->base;\n\n\t\treset_output_state(state);\n\t\twlr_output_state_set_enabled(state, false);\n\t}\n}\n\nstatic bool search_finish(struct search_context *ctx, size_t output_idx) {\n\tstruct wlr_backend_output_state *backend_state = &ctx->states[output_idx];\n\tstruct wlr_output_state *state = &backend_state->base;\n\tstruct wlr_output *wlr_output = backend_state->output;\n\n\tclear_later_output_states(ctx->states, ctx->configs_len, output_idx);\n\tdump_output_state(wlr_output, state);\n\treturn wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&\n\t\tsearch_valid_config(ctx, output_idx+1);\n}\n\nstatic bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {\n\tstruct matched_output_config *cfg = &ctx->configs[output_idx];\n\tstruct wlr_backend_output_state *backend_state = &ctx->states[output_idx];\n\tstruct wlr_output_state *state = &backend_state->base;\n\n\tif (!backend_state->output->adaptive_sync_supported) {\n\t\treturn search_finish(ctx, output_idx);\n\t}\n\n\tif (cfg->config && cfg->config->adaptive_sync == 1) {\n\t\twlr_output_state_set_adaptive_sync_enabled(state, true);\n\t\tif (search_finish(ctx, output_idx)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\twlr_output_state_set_adaptive_sync_enabled(state, false);\n\treturn search_finish(ctx, output_idx);\n}\n\nstatic bool search_mode(struct search_context *ctx, size_t output_idx) {\n\tstruct matched_output_config *cfg = &ctx->configs[output_idx];\n\tstruct wlr_backend_output_state *backend_state = &ctx->states[output_idx];\n\tstruct wlr_output_state *state = &backend_state->base;\n\tstruct wlr_output *wlr_output = backend_state->output;\n\n\t// We only search for mode if one is not explicitly specified in the config\n\tif (config_has_manual_mode(cfg->config)) {\n\t\treturn search_adaptive_sync(ctx, output_idx);\n\t}\n\n\tstruct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);\n\tif (preferred_mode) {\n\t\twlr_output_state_set_mode(state, preferred_mode);\n\t\tif (search_adaptive_sync(ctx, output_idx)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (wl_list_empty(&wlr_output->modes)) {\n\t\tstate->committed &= ~WLR_OUTPUT_STATE_MODE;\n\t\treturn search_adaptive_sync(ctx, output_idx);\n\t}\n\n\tstruct wlr_output_mode *mode;\n\twl_list_for_each(mode, &backend_state->output->modes, link) {\n\t\tif (mode == preferred_mode) {\n\t\t\tcontinue;\n\t\t}\n\t\twlr_output_state_set_mode(state, mode);\n\t\tif (search_adaptive_sync(ctx, output_idx)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic bool search_render_format(struct search_context *ctx, size_t output_idx) {\n\tstruct matched_output_config *cfg = &ctx->configs[output_idx];\n\tstruct wlr_backend_output_state *backend_state = &ctx->states[output_idx];\n\tstruct wlr_output_state *state = &backend_state->base;\n\tstruct wlr_output *wlr_output = backend_state->output;\n\n\tuint32_t fmts[] = {\n\t\tDRM_FORMAT_XRGB2101010,\n\t\tDRM_FORMAT_XBGR2101010,\n\t\tDRM_FORMAT_XRGB8888,\n\t\tDRM_FORMAT_ARGB8888,\n\t\tDRM_FORMAT_RGB565,\n\t\tDRM_FORMAT_INVALID,\n\t};\n\tif (render_format_is_bgr(wlr_output->render_format)) {\n\t\t// Start with BGR in the unlikely event that we previously required it.\n\t\tfmts[0] = DRM_FORMAT_XBGR2101010;\n\t\tfmts[1] = DRM_FORMAT_XRGB2101010;\n\t}\n\n\tconst struct wlr_drm_format_set *primary_formats =\n\t\twlr_output_get_primary_formats(wlr_output, server.allocator->buffer_caps);\n\tenum render_bit_depth needed_bits = get_config_render_bit_depth(cfg->config);\n\tfor (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {\n\t\tenum render_bit_depth format_bits = bit_depth_from_format(fmts[idx]);\n\t\tif (needed_bits < format_bits) {\n\t\t\tcontinue;\n\t\t}\n\t\t// If primary_formats is NULL, all formats are supported\n\t\tif (primary_formats && !wlr_drm_format_set_get(primary_formats, fmts[idx])) {\n\t\t\t// This is not a supported format for this output\n\t\t\tcontinue;\n\t\t}\n\t\twlr_output_state_set_render_format(state, fmts[idx]);\n\t\tif (search_mode(ctx, output_idx)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic bool search_valid_config(struct search_context *ctx, size_t output_idx) {\n\tif (output_idx >= ctx->configs_len) {\n\t\t// We reached the end of the search, all good!\n\t\treturn true;\n\t}\n\n\tstruct matched_output_config *cfg = &ctx->configs[output_idx];\n\tstruct wlr_backend_output_state *backend_state = &ctx->states[output_idx];\n\tstruct wlr_output_state *state = &backend_state->base;\n\tstruct wlr_output *wlr_output = backend_state->output;\n\n\tif (!output_config_is_disabling(cfg->config)) {\n\t\t// Search through our possible configurations, doing a depth-first\n\t\t// through render_format, modes, adaptive_sync and the next output's\n\t\t// config.\n\t\tqueue_output_config(cfg->config, cfg->output, &backend_state->base);\n\t\tif (search_render_format(ctx, output_idx)) {\n\t\t\treturn true;\n\t\t} else if (!ctx->degrade_to_off) {\n\t\t\treturn false;\n\t\t}\n\t\t// We could not get anything to work, try to disable this output to see\n\t\t// if we can at least make the outputs before us work.\n\t\tsway_log(SWAY_DEBUG, \"Unable to find valid config with output %s, disabling\",\n\t\t\twlr_output->name);\n\t\treset_output_state(state);\n\t}\n\n\twlr_output_state_set_enabled(state, false);\n\treturn search_finish(ctx, output_idx);\n}\n\nstatic int compare_matched_output_config_priority(const void *a, const void *b) {\n\n\tconst struct matched_output_config *amc = a;\n\tconst struct matched_output_config *bmc = b;\n\tbool a_disabling = output_config_is_disabling(amc->config);\n\tbool b_disabling = output_config_is_disabling(bmc->config);\n\tbool a_enabled = amc->output->enabled;\n\tbool b_enabled = bmc->output->enabled;\n\n\t// We want to give priority to existing enabled outputs. To do so, we want\n\t// the configuration order to be:\n\t// 1. Existing, enabled outputs\n\t// 2. Outputs that need to be enabled\n\t// 3. Disabled or disabling outputs\n\tif (a_enabled && !a_disabling) {\n\t\treturn -1;\n\t} else if (b_enabled && !b_disabling) {\n\t\treturn 1;\n\t} else if (b_disabling && !a_disabling) {\n\t\treturn -1;\n\t} else if (a_disabling && !b_disabling) {\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic void sort_output_configs_by_priority(\n\t\tstruct matched_output_config *configs, size_t configs_len) {\n\tqsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);\n}\n\nstatic bool apply_resolved_output_configs(struct matched_output_config *configs,\n\t\tsize_t configs_len, bool test_only, bool degrade_to_off) {\n\tstruct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));\n\tif (!states) {\n\t\treturn false;\n\t}\n\tstruct config_output_state *config_states = calloc(configs_len, sizeof(*config_states));\n\tif (!config_states) {\n\t\tfree(states);\n\t\treturn false;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Committing %zd outputs\", configs_len);\n\tfor (size_t idx = 0; idx < configs_len; idx++) {\n\t\tstruct matched_output_config *cfg = &configs[idx];\n\t\tstruct wlr_backend_output_state *backend_state = &states[idx];\n\t\tstruct config_output_state *config_state = &config_states[idx];\n\n\t\tbackend_state->output = cfg->output->wlr_output;\n\t\twlr_output_state_init(&backend_state->base);\n\n\t\tqueue_output_config(cfg->config, cfg->output, &backend_state->base);\n\t\tdump_output_state(cfg->output->wlr_output, &backend_state->base);\n\n\t\tconfig_state->color_transform = get_color_profile(cfg->output->wlr_output, cfg->config);\n\t}\n\n\tstruct wlr_output_swapchain_manager swapchain_mgr;\n\twlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);\n\n\tbool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);\n\tif (!ok) {\n\t\tsway_log(SWAY_ERROR, \"Requested backend configuration failed, searching for valid fallbacks\");\n\t\tstruct search_context ctx = {\n\t\t\t.swapchain_mgr = &swapchain_mgr,\n\t\t\t.states = states,\n\t\t\t.configs = configs,\n\t\t\t.configs_len = configs_len,\n\t\t\t.degrade_to_off = degrade_to_off,\n\t\t};\n\t\tif (!search_valid_config(&ctx, 0)) {\n\t\t\tsway_log(SWAY_ERROR, \"Search for valid config failed\");\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tif (test_only) {\n\t\t// The swapchain manager already did a test for us\n\t\tgoto out;\n\t}\n\n\tfor (size_t idx = 0; idx < configs_len; idx++) {\n\t\tstruct matched_output_config *cfg = &configs[idx];\n\t\tstruct wlr_backend_output_state *backend_state = &states[idx];\n\t\tstruct config_output_state *config_state = &config_states[idx];\n\n\t\tstruct wlr_scene_output_state_options opts = {\n\t\t\t.swapchain = wlr_output_swapchain_manager_get_swapchain(\n\t\t\t\t&swapchain_mgr, backend_state->output),\n\t\t\t.color_transform = config_state->color_transform,\n\t\t};\n\t\tstruct wlr_scene_output *scene_output = cfg->output->scene_output;\n\t\tstruct wlr_output_state *state = &backend_state->base;\n\t\tif (!wlr_scene_output_build_state(scene_output, state, &opts)) {\n\t\t\tsway_log(SWAY_ERROR, \"Building output state for '%s' failed\",\n\t\t\t\tbackend_state->output->name);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tok = wlr_backend_commit(server.backend, states, configs_len);\n\tif (!ok) {\n\t\tsway_log(SWAY_ERROR, \"Backend commit failed\");\n\t\tgoto out;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Commit of %zd outputs succeeded\", configs_len);\n\n\twlr_output_swapchain_manager_apply(&swapchain_mgr);\n\n\tfor (size_t idx = 0; idx < configs_len; idx++) {\n\t\tstruct matched_output_config *cfg = &configs[idx];\n\t\tstruct wlr_backend_output_state *backend_state = &states[idx];\n\t\tstruct config_output_state *config_state = &config_states[idx];\n\t\tsway_log(SWAY_DEBUG, \"Finalizing config for %s\",\n\t\t\tcfg->output->wlr_output->name);\n\t\tfinalize_output_config(cfg->config, cfg->output, &backend_state->base, config_state);\n\t}\n\n\t// Output layout being applied in finalize_output_config can shift outputs\n\t// around, so we do a second pass to update positions and arrange.\n\tfor (size_t idx = 0; idx < configs_len; idx++) {\n\t\tstruct matched_output_config *cfg = &configs[idx];\n\t\toutput_update_position(cfg->output);\n\t\tarrange_layers(cfg->output);\n\t}\n\n\tarrange_root();\n\tarrange_locks();\n\tupdate_output_manager_config(&server);\n\ttransaction_commit_dirty();\n\nout:\n\twlr_output_swapchain_manager_finish(&swapchain_mgr);\n\tfor (size_t idx = 0; idx < configs_len; idx++) {\n\t\tstruct wlr_backend_output_state *backend_state = &states[idx];\n\t\twlr_output_state_finish(&backend_state->base);\n\t\tconfig_output_state_finish(&config_states[idx]);\n\t}\n\tfree(states);\n\tfree(config_states);\n\n\t// Reconfigure all devices, since input config may have been applied before\n\t// this output came online, and some config items (like map_to_output) are\n\t// dependent on an output being present.\n\tinput_manager_configure_all_input_mappings();\n\t// Reconfigure the cursor images, since the scale may have changed.\n\tinput_manager_configure_xcursor();\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n\t\tcursor_rebase(seat->cursor);\n\t}\n\n\treturn ok;\n}\n\nbool apply_output_configs(struct output_config **ocs, size_t ocs_len,\n\t\tbool test_only, bool degrade_to_off) {\n\tsize_t configs_len = wl_list_length(&root->all_outputs);\n\tstruct matched_output_config *configs = calloc(configs_len, sizeof(*configs));\n\tif (!configs) {\n\t\treturn false;\n\t}\n\n\tint config_idx = 0;\n\tstruct sway_output *sway_output;\n\twl_list_for_each(sway_output, &root->all_outputs, link) {\n\t\tif (sway_output == root->fallback_output) {\n\t\t\tconfigs_len--;\n\t\t\tcontinue;\n\t\t}\n\n\t\tstruct matched_output_config *config = &configs[config_idx++];\n\t\tconfig->output = sway_output;\n\t\tconfig->config = find_output_config_from_list(ocs, ocs_len, sway_output);\n\t}\n\n\tsort_output_configs_by_priority(configs, configs_len);\n\tbool ok = apply_resolved_output_configs(configs, configs_len, test_only, degrade_to_off);\n\tfor (size_t idx = 0; idx < configs_len; idx++) {\n\t\tstruct matched_output_config *cfg = &configs[idx];\n\t\tfree_output_config(cfg->config);\n\t}\n\tfree(configs);\n\treturn ok;\n}\n\nvoid apply_stored_output_configs(void) {\n\tapply_output_configs((struct output_config **)config->output_configs->items,\n\t\t\tconfig->output_configs->length, false, true);\n}\n\nvoid free_output_config(struct output_config *oc) {\n\tif (!oc) {\n\t\treturn;\n\t}\n\tfree(oc->name);\n\tfree(oc->background);\n\tfree(oc->background_option);\n\tfree(oc->background_fallback);\n\twlr_color_transform_unref(oc->color_transform);\n\tfree(oc);\n}\n\nstatic void handle_swaybg_client_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_config *sway_config =\n\t\twl_container_of(listener, sway_config, swaybg_client_destroy);\n\twl_list_remove(&sway_config->swaybg_client_destroy.link);\n\twl_list_init(&sway_config->swaybg_client_destroy.link);\n\tsway_config->swaybg_client = NULL;\n}\n\nstatic bool _spawn_swaybg(char **command) {\n\tif (config->swaybg_client != NULL) {\n\t\twl_client_destroy(config->swaybg_client);\n\t}\n\tint sockets[2];\n\tif (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"socketpair failed\");\n\t\treturn false;\n\t}\n\tif (!sway_set_cloexec(sockets[0], true) || !sway_set_cloexec(sockets[1], true)) {\n\t\treturn false;\n\t}\n\n\tconfig->swaybg_client = wl_client_create(server.wl_display, sockets[0]);\n\tif (config->swaybg_client == NULL) {\n\t\tsway_log_errno(SWAY_ERROR, \"wl_client_create failed\");\n\t\treturn false;\n\t}\n\n\tconfig->swaybg_client_destroy.notify = handle_swaybg_client_destroy;\n\twl_client_add_destroy_listener(config->swaybg_client,\n\t\t&config->swaybg_client_destroy);\n\n\tpid_t pid = fork();\n\tif (pid < 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"fork failed\");\n\t\treturn false;\n\t} else if (pid == 0) {\n\t\tif (!sway_set_cloexec(sockets[1], false)) {\n\t\t\t_exit(EXIT_FAILURE);\n\t\t}\n\n\t\tchar wayland_socket_str[16];\n\t\tsnprintf(wayland_socket_str, sizeof(wayland_socket_str),\n\t\t\t\"%d\", sockets[1]);\n\t\tsetenv(\"WAYLAND_SOCKET\", wayland_socket_str, true);\n\n\t\texecvp(command[0], command);\n\t\tsway_log_errno(SWAY_ERROR, \"failed to execute '%s' \"\n\t\t\t\"(background configuration probably not applied)\",\n\t\t\tcommand[0]);\n\t\t_exit(EXIT_FAILURE);\n\t}\n\n\tif (close(sockets[1]) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"close failed\");\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nbool spawn_swaybg(void) {\n\tif (!config->swaybg_command) {\n\t\treturn true;\n\t}\n\n\tsize_t length = 2;\n\tfor (int i = 0; i < config->output_configs->length; i++) {\n\t\tstruct output_config *oc = config->output_configs->items[i];\n\t\tif (!oc->background) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (strcmp(oc->background_option, \"solid_color\") == 0) {\n\t\t\tlength += 4;\n\t\t} else if (oc->background_fallback) {\n\t\t\tlength += 8;\n\t\t} else {\n\t\t\tlength += 6;\n\t\t}\n\t}\n\n\tchar **cmd = calloc(length, sizeof(char *));\n\tif (!cmd) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate spawn_swaybg command\");\n\t\treturn false;\n\t}\n\n\tsize_t i = 0;\n\tcmd[i++] = config->swaybg_command;\n\tfor (int j = 0; j < config->output_configs->length; j++) {\n\t\tstruct output_config *oc = config->output_configs->items[j];\n\t\tif (!oc->background) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (strcmp(oc->background_option, \"solid_color\") == 0) {\n\t\t\tcmd[i++] = \"-o\";\n\t\t\tcmd[i++] = oc->name;\n\t\t\tcmd[i++] = \"-c\";\n\t\t\tcmd[i++] = oc->background;\n\t\t} else {\n\t\t\tcmd[i++] = \"-o\";\n\t\t\tcmd[i++] = oc->name;\n\t\t\tcmd[i++] = \"-i\";\n\t\t\tcmd[i++] = oc->background;\n\t\t\tcmd[i++] = \"-m\";\n\t\t\tcmd[i++] = oc->background_option;\n\t\t\tif (oc->background_fallback) {\n\t\t\t\tcmd[i++] = \"-c\";\n\t\t\t\tcmd[i++] = oc->background_fallback;\n\t\t\t}\n\t\t}\n\t\tassert(i <= length);\n\t}\n\n\tfor (size_t k = 0; k < i; k++) {\n\t\tsway_log(SWAY_DEBUG, \"spawn_swaybg cmd[%zd] = %s\", k, cmd[k]);\n\t}\n\n\tbool result = _spawn_swaybg(cmd);\n\tfree(cmd);\n\treturn result;\n}\n"
  },
  {
    "path": "sway/config/seat.c",
    "content": "#include <limits.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"sway/config.h\"\n#include \"log.h\"\n\nstruct seat_config *new_seat_config(const char* name) {\n\tstruct seat_config *seat = calloc(1, sizeof(struct seat_config));\n\tif (!seat) {\n\t\tsway_log(SWAY_DEBUG, \"Unable to allocate seat config\");\n\t\treturn NULL;\n\t}\n\n\tseat->name = strdup(name);\n\tif (!sway_assert(seat->name, \"could not allocate name for seat\")) {\n\t\tfree(seat);\n\t\treturn NULL;\n\t}\n\n\tseat->idle_inhibit_sources = seat->idle_wake_sources = UINT32_MAX;\n\n\tseat->fallback = -1;\n\tseat->attachments = create_list();\n\tif (!sway_assert(seat->attachments,\n\t\t\t\t\"could not allocate seat attachments list\")) {\n\t\tfree(seat->name);\n\t\tfree(seat);\n\t\treturn NULL;\n\t}\n\tseat->hide_cursor_timeout = -1;\n\tseat->hide_cursor_when_typing = HIDE_WHEN_TYPING_DEFAULT;\n\tseat->allow_constrain = CONSTRAIN_DEFAULT;\n\tseat->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;\n\tseat->keyboard_grouping = KEYBOARD_GROUP_DEFAULT;\n\tseat->xcursor_theme.name = NULL;\n\tseat->xcursor_theme.size = 24;\n\n\treturn seat;\n}\n\nstatic void merge_wildcard_on_all(struct seat_config *wildcard) {\n\tfor (int i = 0; i < config->seat_configs->length; i++) {\n\t\tstruct seat_config *sc = config->seat_configs->items[i];\n\t\tif (strcmp(wildcard->name, sc->name) != 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Merging seat * config on %s\", sc->name);\n\t\t\tmerge_seat_config(sc, wildcard);\n\t\t}\n\t}\n}\n\nstruct seat_config *store_seat_config(struct seat_config *sc) {\n\tbool wildcard = strcmp(sc->name, \"*\") == 0;\n\tif (wildcard) {\n\t\tmerge_wildcard_on_all(sc);\n\t}\n\n\tint i = list_seq_find(config->seat_configs, seat_name_cmp, sc->name);\n\tif (i >= 0) {\n\t\tsway_log(SWAY_DEBUG, \"Merging on top of existing seat config\");\n\t\tstruct seat_config *current = config->seat_configs->items[i];\n\t\tmerge_seat_config(current, sc);\n\t\tfree_seat_config(sc);\n\t\tsc = current;\n\t} else if (!wildcard) {\n\t\tsway_log(SWAY_DEBUG, \"Adding non-wildcard seat config\");\n\t\ti = list_seq_find(config->seat_configs, seat_name_cmp, \"*\");\n\t\tif (i >= 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"Merging on top of seat * config\");\n\t\t\tstruct seat_config *current = new_seat_config(sc->name);\n\t\t\tmerge_seat_config(current, config->seat_configs->items[i]);\n\t\t\tmerge_seat_config(current, sc);\n\t\t\tfree_seat_config(sc);\n\t\t\tsc = current;\n\t\t}\n\t\tlist_add(config->seat_configs, sc);\n\t} else {\n\t\t// New wildcard config. Just add it\n\t\tsway_log(SWAY_DEBUG, \"Adding seat * config\");\n\t\tlist_add(config->seat_configs, sc);\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Config stored for seat %s\", sc->name);\n\n\treturn sc;\n}\n\nstruct seat_attachment_config *seat_attachment_config_new(void) {\n\tstruct seat_attachment_config *attachment =\n\t\tcalloc(1, sizeof(struct seat_attachment_config));\n\tif (!attachment) {\n\t\tsway_log(SWAY_DEBUG, \"cannot allocate attachment config\");\n\t\treturn NULL;\n\t}\n\treturn attachment;\n}\n\nstatic void seat_attachment_config_free(\n\t\tstruct seat_attachment_config *attachment) {\n\tfree(attachment->identifier);\n\tfree(attachment);\n}\n\nstatic struct seat_attachment_config *seat_attachment_config_copy(\n\t\tstruct seat_attachment_config *attachment) {\n\tstruct seat_attachment_config *copy = seat_attachment_config_new();\n\tif (!copy) {\n\t\treturn NULL;\n\t}\n\n\tcopy->identifier = strdup(attachment->identifier);\n\n\treturn copy;\n}\n\nstatic void merge_seat_attachment_config(struct seat_attachment_config *dest,\n\t\tstruct seat_attachment_config *source) {\n\t// nothing to merge yet, but there will be some day\n}\n\nvoid merge_seat_config(struct seat_config *dest, struct seat_config *source) {\n\tif (source->fallback != -1) {\n\t\tdest->fallback = source->fallback;\n\t}\n\n\tfor (int i = 0; i < source->attachments->length; ++i) {\n\t\tstruct seat_attachment_config *source_attachment =\n\t\t\tsource->attachments->items[i];\n\t\tbool found = false;\n\t\tfor (int j = 0; j < dest->attachments->length; ++j) {\n\t\t\tstruct seat_attachment_config *dest_attachment =\n\t\t\t\tdest->attachments->items[j];\n\t\t\tif (strcmp(source_attachment->identifier,\n\t\t\t\t\t\tdest_attachment->identifier) == 0) {\n\t\t\t\tmerge_seat_attachment_config(dest_attachment,\n\t\t\t\t\tsource_attachment);\n\t\t\t\tfound = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!found) {\n\t\t\tstruct seat_attachment_config *copy =\n\t\t\t\tseat_attachment_config_copy(source_attachment);\n\t\t\tif (copy) {\n\t\t\t\tlist_add(dest->attachments, copy);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (source->hide_cursor_timeout != -1) {\n\t\tdest->hide_cursor_timeout = source->hide_cursor_timeout;\n\t}\n\n\tif (source->hide_cursor_when_typing != HIDE_WHEN_TYPING_DEFAULT) {\n\t\tdest->hide_cursor_when_typing = source->hide_cursor_when_typing;\n\t}\n\n\tif (source->allow_constrain != CONSTRAIN_DEFAULT) {\n\t\tdest->allow_constrain = source->allow_constrain;\n\t}\n\n\tif (source->shortcuts_inhibit != SHORTCUTS_INHIBIT_DEFAULT) {\n\t\tdest->shortcuts_inhibit = source->shortcuts_inhibit;\n\t}\n\n\tif (source->keyboard_grouping != KEYBOARD_GROUP_DEFAULT) {\n\t\tdest->keyboard_grouping = source->keyboard_grouping;\n\t}\n\n\tif (source->xcursor_theme.name != NULL) {\n\t\tfree(dest->xcursor_theme.name);\n\t\tdest->xcursor_theme.name = strdup(source->xcursor_theme.name);\n\t\tdest->xcursor_theme.size = source->xcursor_theme.size;\n\t}\n\n\tif (source->idle_inhibit_sources != UINT32_MAX) {\n\t\tdest->idle_inhibit_sources = source->idle_inhibit_sources;\n\t}\n\n\tif (source->idle_wake_sources != UINT32_MAX) {\n\t\tdest->idle_wake_sources = source->idle_wake_sources;\n\t}\n}\n\nstruct seat_config *copy_seat_config(struct seat_config *seat) {\n\tstruct seat_config *copy = new_seat_config(seat->name);\n\tif (copy == NULL) {\n\t\treturn NULL;\n\t}\n\n\tmerge_seat_config(copy, seat);\n\n\treturn copy;\n}\n\nvoid free_seat_config(struct seat_config *seat) {\n\tif (!seat) {\n\t\treturn;\n\t}\n\n\tfree(seat->name);\n\tfor (int i = 0; i < seat->attachments->length; ++i) {\n\t\tseat_attachment_config_free(seat->attachments->items[i]);\n\t}\n\tlist_free(seat->attachments);\n\tfree(seat->xcursor_theme.name);\n\tfree(seat);\n}\n\nint seat_name_cmp(const void *item, const void *data) {\n\tconst struct seat_config *sc = item;\n\tconst char *name = data;\n\treturn strcmp(sc->name, name);\n}\n\nstruct seat_attachment_config *seat_config_get_attachment(\n\t\tstruct seat_config *seat_config, char *identifier) {\n\tfor (int i = 0; i < seat_config->attachments->length; ++i) {\n\t\tstruct seat_attachment_config *attachment =\n\t\t\tseat_config->attachments->items[i];\n\t\tif (strcmp(attachment->identifier, identifier) == 0) {\n\t\t\treturn attachment;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/config.c",
    "content": "#undef _POSIX_C_SOURCE\n#define _XOPEN_SOURCE 700 // for realpath\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <libgen.h>\n#include <wordexp.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <sys/stat.h>\n#include <signal.h>\n#include <libinput.h>\n#include <limits.h>\n#include <dirent.h>\n#include <strings.h>\n#include <linux/input-event-codes.h>\n#include <wlr/types/wlr_output.h>\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/input/switch.h\"\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/criteria.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/server.h\"\n#include \"sway/swaynag.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/workspace.h\"\n#include \"cairo_util.h\"\n#include \"pango.h\"\n#include \"stringop.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct sway_config *config = NULL;\n\nstatic struct xkb_state *keysym_translation_state_create(\n\t\tstruct xkb_rule_names rules, uint32_t context_flags) {\n\tstruct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV);\n\tstruct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names(\n\t\tcontext,\n\t\t&rules,\n\t\tXKB_KEYMAP_COMPILE_NO_FLAGS);\n\txkb_context_unref(context);\n\tif (xkb_keymap == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Failed to compile keysym translation XKB keymap\");\n\t\treturn NULL;\n\t}\n\n\treturn xkb_state_new(xkb_keymap);\n}\n\nstatic void keysym_translation_state_destroy(\n\t\tstruct xkb_state *state) {\n\tif (state == NULL) {\n\t\treturn;\n\t}\n\txkb_keymap_unref(xkb_state_get_keymap(state));\n\txkb_state_unref(state);\n}\n\nstatic void free_mode(struct sway_mode *mode) {\n\tif (!mode) {\n\t\treturn;\n\t}\n\tfree(mode->name);\n\tif (mode->keysym_bindings) {\n\t\tfor (int i = 0; i < mode->keysym_bindings->length; i++) {\n\t\t\tfree_sway_binding(mode->keysym_bindings->items[i]);\n\t\t}\n\t\tlist_free(mode->keysym_bindings);\n\t}\n\tif (mode->keycode_bindings) {\n\t\tfor (int i = 0; i < mode->keycode_bindings->length; i++) {\n\t\t\tfree_sway_binding(mode->keycode_bindings->items[i]);\n\t\t}\n\t\tlist_free(mode->keycode_bindings);\n\t}\n\tif (mode->mouse_bindings) {\n\t\tfor (int i = 0; i < mode->mouse_bindings->length; i++) {\n\t\t\tfree_sway_binding(mode->mouse_bindings->items[i]);\n\t\t}\n\t\tlist_free(mode->mouse_bindings);\n\t}\n\tif (mode->switch_bindings) {\n\t\tfor (int i = 0; i < mode->switch_bindings->length; i++) {\n\t\t\tfree_switch_binding(mode->switch_bindings->items[i]);\n\t\t}\n\t\tlist_free(mode->switch_bindings);\n\t}\n\tif (mode->gesture_bindings) {\n\t\tfor (int i = 0; i < mode->gesture_bindings->length; i++) {\n\t\t\tfree_gesture_binding(mode->gesture_bindings->items[i]);\n\t\t}\n\t\tlist_free(mode->gesture_bindings);\n\t}\n\tfree(mode);\n}\n\nvoid free_config(struct sway_config *config) {\n\tif (!config) {\n\t\treturn;\n\t}\n\n\tmemset(&config->handler_context, 0, sizeof(config->handler_context));\n\n\t// TODO: handle all currently unhandled lists as we add implementations\n\tif (config->symbols) {\n\t\tfor (int i = 0; i < config->symbols->length; ++i) {\n\t\t\tfree_sway_variable(config->symbols->items[i]);\n\t\t}\n\t\tlist_free(config->symbols);\n\t}\n\tif (config->modes) {\n\t\tfor (int i = 0; i < config->modes->length; ++i) {\n\t\t\tfree_mode(config->modes->items[i]);\n\t\t}\n\t\tlist_free(config->modes);\n\t}\n\tif (config->bars) {\n\t\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\t\tfree_bar_config(config->bars->items[i]);\n\t\t}\n\t\tlist_free(config->bars);\n\t}\n\tlist_free(config->cmd_queue);\n\tif (config->workspace_configs) {\n\t\tfor (int i = 0; i < config->workspace_configs->length; i++) {\n\t\t\tfree_workspace_config(config->workspace_configs->items[i]);\n\t\t}\n\t\tlist_free(config->workspace_configs);\n\t}\n\tif (config->output_configs) {\n\t\tfor (int i = 0; i < config->output_configs->length; i++) {\n\t\t\tfree_output_config(config->output_configs->items[i]);\n\t\t}\n\t\tlist_free(config->output_configs);\n\t}\n\tif (config->swaybg_client != NULL) {\n\t\twl_client_destroy(config->swaybg_client);\n\t}\n\tif (config->input_configs) {\n\t\tfor (int i = 0; i < config->input_configs->length; i++) {\n\t\t\tfree_input_config(config->input_configs->items[i]);\n\t\t}\n\t\tlist_free(config->input_configs);\n\t}\n\tif (config->input_type_configs) {\n\t\tfor (int i = 0; i < config->input_type_configs->length; i++) {\n\t\t\tfree_input_config(config->input_type_configs->items[i]);\n\t\t}\n\t\tlist_free(config->input_type_configs);\n\t}\n\tif (config->seat_configs) {\n\t\tfor (int i = 0; i < config->seat_configs->length; i++) {\n\t\t\tfree_seat_config(config->seat_configs->items[i]);\n\t\t}\n\t\tlist_free(config->seat_configs);\n\t}\n\tif (config->criteria) {\n\t\tfor (int i = 0; i < config->criteria->length; ++i) {\n\t\t\tcriteria_destroy(config->criteria->items[i]);\n\t\t}\n\t\tlist_free(config->criteria);\n\t}\n\tlist_free(config->no_focus);\n\tlist_free(config->active_bar_modifiers);\n\tlist_free_items_and_destroy(config->config_chain);\n\tfree(config->floating_scroll_up_cmd);\n\tfree(config->floating_scroll_down_cmd);\n\tfree(config->floating_scroll_left_cmd);\n\tfree(config->floating_scroll_right_cmd);\n\tfree(config->font);\n\tfree(config->swaybg_command);\n\tfree(config->swaynag_command);\n\tfree((char *)config->current_config_path);\n\tfree((char *)config->current_config);\n\tkeysym_translation_state_destroy(config->keysym_translation_state);\n\tfree(config);\n}\n\nstatic void destroy_removed_seats(struct sway_config *old_config,\n\t\tstruct sway_config *new_config) {\n\tstruct seat_config *seat_config;\n\tstruct sway_seat *seat;\n\tint i;\n\tfor (i = 0; i < old_config->seat_configs->length; i++) {\n\t\tseat_config = old_config->seat_configs->items[i];\n\t\t// Skip the wildcard seat config, it won't have a matching real seat.\n\t\tif (strcmp(seat_config->name, \"*\") == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* Also destroy seats that aren't present in new config */\n\t\tif (new_config && list_seq_find(new_config->seat_configs,\n\t\t\t\tseat_name_cmp, seat_config->name) < 0) {\n\t\t\tseat = input_manager_get_seat(seat_config->name, false);\n\t\t\tif (seat) {\n\t\t\t\tseat_destroy(seat);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void config_defaults(struct sway_config *config) {\n\tif (!(config->swaynag_command = strdup(\"swaynag\"))) goto cleanup;\n\tconfig->swaynag_config_errors = (struct swaynag_instance){0};\n\tconfig->swaynag_config_errors.args = \"--type error \"\n\t\t\t\"--message 'There are errors in your config file' \"\n\t\t\t\"--detailed-message \"\n\t\t\t\"--button-no-terminal 'Exit sway' 'swaymsg exit' \"\n\t\t\t\"--button-no-terminal 'Reload sway' 'swaymsg reload'\";\n\tconfig->swaynag_config_errors.detailed = true;\n\n\tif (!(config->symbols = create_list())) goto cleanup;\n\tif (!(config->modes = create_list())) goto cleanup;\n\tif (!(config->bars = create_list())) goto cleanup;\n\tif (!(config->workspace_configs = create_list())) goto cleanup;\n\tif (!(config->criteria = create_list())) goto cleanup;\n\tif (!(config->no_focus = create_list())) goto cleanup;\n\tif (!(config->seat_configs = create_list())) goto cleanup;\n\tif (!(config->output_configs = create_list())) goto cleanup;\n\n\tif (!(config->input_type_configs = create_list())) goto cleanup;\n\tif (!(config->input_configs = create_list())) goto cleanup;\n\n\tif (!(config->cmd_queue = create_list())) goto cleanup;\n\n\tif (!(config->current_mode = malloc(sizeof(struct sway_mode))))\n\t\tgoto cleanup;\n\tif (!(config->current_mode->name = malloc(sizeof(\"default\")))) goto cleanup;\n\tstrcpy(config->current_mode->name, \"default\");\n\tif (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;\n\tif (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;\n\tif (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;\n\tif (!(config->current_mode->switch_bindings = create_list())) goto cleanup;\n\tif (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;\n\tlist_add(config->modes, config->current_mode);\n\n\tconfig->floating_mod = 0;\n\tconfig->floating_mod_inverse = false;\n\tconfig->dragging_key = BTN_LEFT;\n\tconfig->resizing_key = BTN_RIGHT;\n\n\tif (!(config->floating_scroll_up_cmd = strdup(\"\"))) goto cleanup;\n\tif (!(config->floating_scroll_down_cmd = strdup(\"\"))) goto cleanup;\n\tif (!(config->floating_scroll_left_cmd = strdup(\"\"))) goto cleanup;\n\tif (!(config->floating_scroll_right_cmd = strdup(\"\"))) goto cleanup;\n\tconfig->default_layout = L_NONE;\n\tconfig->default_orientation = L_NONE;\n\tif (!(config->font = strdup(\"monospace 10\"))) goto cleanup;\n\tconfig->font_description = pango_font_description_from_string(config->font);\n\tconfig->urgent_timeout = 500;\n\tconfig->focus_on_window_activation = FOWA_URGENT;\n\tconfig->popup_during_fullscreen = POPUP_SMART;\n\tconfig->xwayland = XWAYLAND_MODE_LAZY;\n\n\tconfig->titlebar_border_thickness = 1;\n\tconfig->titlebar_h_padding = 5;\n\tconfig->titlebar_v_padding = 4;\n\n\t// floating view\n\tconfig->floating_maximum_width = 0;\n\tconfig->floating_maximum_height = 0;\n\tconfig->floating_minimum_width = 75;\n\tconfig->floating_minimum_height = 50;\n\n\t// Flags\n\tconfig->focus_follows_mouse = FOLLOWS_YES;\n\tconfig->mouse_warping = WARP_OUTPUT;\n\tconfig->focus_wrapping = WRAP_YES;\n\tconfig->validating = false;\n\tconfig->reloading = false;\n\tconfig->active = false;\n\tconfig->failed = false;\n\tconfig->auto_back_and_forth = false;\n\tconfig->reading = false;\n\tconfig->show_marks = true;\n\tconfig->title_align = ALIGN_LEFT;\n\tconfig->tiling_drag = true;\n\tconfig->tiling_drag_threshold = 9;\n\tconfig->primary_selection = true;\n\n\tconfig->smart_gaps = SMART_GAPS_OFF;\n\tconfig->gaps_inner = 0;\n\tconfig->gaps_outer.top = 0;\n\tconfig->gaps_outer.right = 0;\n\tconfig->gaps_outer.bottom = 0;\n\tconfig->gaps_outer.left = 0;\n\n\tif (!(config->active_bar_modifiers = create_list())) goto cleanup;\n\n\tif (!(config->swaybg_command = strdup(\"swaybg\"))) goto cleanup;\n\n\tif (!(config->config_chain = create_list())) goto cleanup;\n\tconfig->current_config_path = NULL;\n\tconfig->current_config = NULL;\n\n\t// borders\n\tconfig->border = B_NORMAL;\n\tconfig->floating_border = B_NORMAL;\n\tconfig->border_thickness = 2;\n\tconfig->floating_border_thickness = 2;\n\tconfig->hide_edge_borders = E_NONE;\n\tconfig->hide_edge_borders_smart = ESMART_OFF;\n\tconfig->hide_lone_tab = false;\n\n\tconfig->has_focused_tab_title = false;\n\n\t// border colors\n\tcolor_to_rgba(config->border_colors.focused.border, 0x4C7899FF);\n\tcolor_to_rgba(config->border_colors.focused.background, 0x285577FF);\n\tcolor_to_rgba(config->border_colors.focused.text, 0xFFFFFFFF);\n\tcolor_to_rgba(config->border_colors.focused.indicator, 0x2E9EF4FF);\n\tcolor_to_rgba(config->border_colors.focused.child_border, 0x285577FF);\n\n\tcolor_to_rgba(config->border_colors.focused_inactive.border, 0x333333FF);\n\tcolor_to_rgba(config->border_colors.focused_inactive.background, 0x5F676AFF);\n\tcolor_to_rgba(config->border_colors.focused_inactive.text, 0xFFFFFFFF);\n\tcolor_to_rgba(config->border_colors.focused_inactive.indicator, 0x484E50FF);\n\tcolor_to_rgba(config->border_colors.focused_inactive.child_border, 0x5F676AFF);\n\n\tcolor_to_rgba(config->border_colors.unfocused.border, 0x333333FF);\n\tcolor_to_rgba(config->border_colors.unfocused.background, 0x222222FF);\n\tcolor_to_rgba(config->border_colors.unfocused.text, 0x888888FF);\n\tcolor_to_rgba(config->border_colors.unfocused.indicator, 0x292D2EFF);\n\tcolor_to_rgba(config->border_colors.unfocused.child_border, 0x222222FF);\n\n\tcolor_to_rgba(config->border_colors.urgent.border, 0x2F343AFF);\n\tcolor_to_rgba(config->border_colors.urgent.background, 0x900000FF);\n\tcolor_to_rgba(config->border_colors.urgent.text, 0xFFFFFFFF);\n\tcolor_to_rgba(config->border_colors.urgent.indicator, 0x900000FF);\n\tcolor_to_rgba(config->border_colors.urgent.child_border, 0x900000FF);\n\n\tcolor_to_rgba(config->border_colors.placeholder.border, 0x000000FF);\n\tcolor_to_rgba(config->border_colors.placeholder.background, 0x0C0C0CFF);\n\tcolor_to_rgba(config->border_colors.placeholder.text, 0xFFFFFFFF);\n\tcolor_to_rgba(config->border_colors.placeholder.indicator, 0x000000FF);\n\tcolor_to_rgba(config->border_colors.placeholder.child_border, 0x0C0C0CFF);\n\n\tcolor_to_rgba(config->border_colors.background, 0xFFFFFFFF);\n\n\t// The keysym to keycode translation\n\tstruct xkb_rule_names rules = {0};\n\tconfig->keysym_translation_state = keysym_translation_state_create(rules, 0);\n\tif (config->keysym_translation_state == NULL) {\n\t\tconfig->keysym_translation_state = keysym_translation_state_create(rules,\n\t\t\tXKB_CONTEXT_NO_ENVIRONMENT_NAMES);\n\t}\n\tif (config->keysym_translation_state == NULL) {\n\t\tgoto cleanup;\n\t}\n\n\treturn;\ncleanup:\n\tsway_abort(\"Unable to allocate config structures\");\n}\n\nstatic bool file_exists(const char *path) {\n\treturn path && access(path, R_OK) != -1;\n}\n\nstatic char *config_path(const char *prefix, const char *config_folder) {\n\tif (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {\n\t\treturn NULL;\n\t}\n\treturn format_str(\"%s/%s/config\", prefix, config_folder);\n}\n\nstatic char *get_config_path(void) {\n\tchar *path = NULL;\n\tconst char *home = getenv(\"HOME\");\n\tchar *config_home_fallback = NULL;\n\n\tconst char *config_home = getenv(\"XDG_CONFIG_HOME\");\n\tif ((config_home == NULL || config_home[0] == '\\0') && home != NULL) {\n\t\tconfig_home_fallback = format_str(\"%s/.config\", home);\n\t\tconfig_home = config_home_fallback;\n\t}\n\n\tstruct config_path {\n\t\tconst char *prefix;\n\t\tconst char *config_folder;\n\t};\n\n\tstruct config_path config_paths[] = {\n\t\t{ .prefix = home, .config_folder = \".sway\"},\n\t\t{ .prefix = config_home, .config_folder = \"sway\"},\n\t\t{ .prefix = home, .config_folder = \".i3\"},\n\t\t{ .prefix = config_home, .config_folder = \"i3\"},\n\t\t{ .prefix = SYSCONFDIR, .config_folder = \"sway\"},\n\t\t{ .prefix = SYSCONFDIR, .config_folder = \"i3\"}\n\t};\n\n\tsize_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]);\n\tfor (size_t i = 0; i < num_config_paths; i++) {\n\t\tpath = config_path(config_paths[i].prefix, config_paths[i].config_folder);\n\t\tif (!path) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (file_exists(path)) {\n\t\t\tbreak;\n\t\t}\n\t\tfree(path);\n\t\tpath = NULL;\n\t}\n\n\tfree(config_home_fallback);\n\treturn path;\n}\n\nstatic bool load_config(const char *path, struct sway_config *config,\n\t\tstruct swaynag_instance *swaynag) {\n\tif (path == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Unable to find a config file!\");\n\t\treturn false;\n\t}\n\n\tsway_log(SWAY_INFO, \"Loading config from %s\", path);\n\n\tstruct stat sb;\n\tif (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {\n\t\tsway_log(SWAY_ERROR, \"%s is a directory not a config file\", path);\n\t\treturn false;\n\t}\n\n\tFILE *f = fopen(path, \"r\");\n\tif (!f) {\n\t\tsway_log(SWAY_ERROR, \"Unable to open %s for reading\", path);\n\t\treturn false;\n\t}\n\n\tbool config_load_success = read_config(f, config, swaynag);\n\tfclose(f);\n\n\tif (!config_load_success) {\n\t\tsway_log(SWAY_ERROR, \"Error(s) loading config!\");\n\t}\n\n\treturn config->active || !config->validating || config_load_success;\n}\n\nbool load_main_config(const char *file, bool is_active, bool validating) {\n\tchar *path;\n\tif (file != NULL) {\n\t\tpath = strdup(file);\n\t} else {\n\t\tpath = get_config_path();\n\t}\n\tif (path == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Cannot find config file\");\n\t\treturn false;\n\t}\n\n\tchar *real_path = realpath(path, NULL);\n\tif (real_path == NULL) {\n\t\tsway_log(SWAY_ERROR, \"%s not found\", path);\n\t\tfree(path);\n\t\treturn false;\n\t}\n\n\tstruct sway_config *old_config = config;\n\tconfig = calloc(1, sizeof(struct sway_config));\n\tif (!config) {\n\t\tsway_abort(\"Unable to allocate config\");\n\t}\n\n\tconfig_defaults(config);\n\tconfig->validating = validating;\n\tif (is_active) {\n\t\tsway_log(SWAY_DEBUG, \"Performing configuration file %s\",\n\t\t\tvalidating ? \"validation\" : \"reload\");\n\t\tconfig->reloading = true;\n\t\tconfig->active = true;\n\n\t\t// xwayland can only be enabled/disabled at launch\n\t\tsway_log(SWAY_DEBUG, \"xwayland will remain %s\",\n\t\t\t\told_config->xwayland ? \"enabled\" : \"disabled\");\n\t\tconfig->xwayland = old_config->xwayland;\n\n\t\t// primary_selection can only be enabled/disabled at launch\n\t\tsway_log(SWAY_DEBUG, \"primary_selection will remain %s\",\n\t\t\t\told_config->primary_selection ? \"enabled\" : \"disabled\");\n\t\tconfig->primary_selection = old_config->primary_selection;\n\n\t\tif (!config->validating) {\n\t\t\tif (old_config->swaybg_client != NULL) {\n\t\t\t\twl_client_destroy(old_config->swaybg_client);\n\t\t\t}\n\n\t\t\tif (old_config->swaynag_config_errors.client != NULL) {\n\t\t\t\twl_client_destroy(old_config->swaynag_config_errors.client);\n\t\t\t}\n\n\t\t\tinput_manager_reset_all_inputs();\n\t\t}\n\t}\n\n\tconfig->user_config_path = file ? true : false;\n\tconfig->current_config_path = path;\n\tlist_add(config->config_chain, real_path);\n\n\tconfig->reading = true;\n\n\tbool success = load_config(path, config, &config->swaynag_config_errors);\n\n\tif (validating) {\n\t\tfree_config(config);\n\t\tconfig = old_config;\n\t\treturn success;\n\t}\n\n\t// Only really necessary if not explicitly `font` is set in the config.\n\tconfig_update_font_height();\n\n\tif (!validating) {\n\t\tinput_manager_verify_fallback_seat();\n\n\t\tfor (int i = 0; i < config->input_configs->length; i++) {\n\t\t\tinput_manager_apply_input_config(config->input_configs->items[i]);\n\t\t}\n\n\t\tfor (int i = 0; i < config->input_type_configs->length; i++) {\n\t\t\tinput_manager_apply_input_config(\n\t\t\t\t\tconfig->input_type_configs->items[i]);\n\t\t}\n\n\t\tfor (int i = 0; i < config->seat_configs->length; i++) {\n\t\t\tinput_manager_apply_seat_config(config->seat_configs->items[i]);\n\t\t}\n\t\tsway_switch_retrigger_bindings_for_all();\n\n\t\tspawn_swaybg();\n\n\t\tconfig->reloading = false;\n\t\tif (is_active) {\n\t\t\trequest_modeset();\n\t\t\tif (config->swaynag_config_errors.client != NULL) {\n\t\t\t\tswaynag_show(&config->swaynag_config_errors);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (old_config) {\n\t\tdestroy_removed_seats(old_config, config);\n\t\tfree_config(old_config);\n\t}\n\tconfig->reading = false;\n\treturn success;\n}\n\nstatic bool load_include_config(const char *path, struct sway_config *config,\n\t\tstruct swaynag_instance *swaynag) {\n\t// save parent config\n\tconst char *parent_config = config->current_config_path;\n\n\tchar *real_path = realpath(path, NULL);\n\n\tif (real_path == NULL) {\n\t\tsway_log(SWAY_DEBUG, \"%s not found.\", path);\n\t\treturn false;\n\t}\n\n\t// check if config has already been included\n\tint j;\n\tfor (j = 0; j < config->config_chain->length; ++j) {\n\t\tchar *old_path = config->config_chain->items[j];\n\t\tif (strcmp(real_path, old_path) == 0) {\n\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\"%s already included once, won't be included again.\",\n\t\t\t\treal_path);\n\t\t\tfree(real_path);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tconfig->current_config_path = real_path;\n\tlist_add(config->config_chain, real_path);\n\tint index = config->config_chain->length - 1;\n\n\tif (!load_config(real_path, config, swaynag)) {\n\t\tfree(real_path);\n\t\tconfig->current_config_path = parent_config;\n\t\tlist_del(config->config_chain, index);\n\t\treturn false;\n\t}\n\n\t// restore current_config_path\n\tconfig->current_config_path = parent_config;\n\treturn true;\n}\n\nvoid load_include_configs(const char *path, struct sway_config *config,\n\t\tstruct swaynag_instance *swaynag) {\n\tchar *wd = getcwd(NULL, 0);\n\tchar *parent_path = strdup(config->current_config_path);\n\tconst char *parent_dir = dirname(parent_path);\n\n\tif (chdir(parent_dir) < 0) {\n\t\tsway_log(SWAY_ERROR, \"failed to change working directory\");\n\t\tgoto cleanup;\n\t}\n\n\twordexp_t p;\n\tif (wordexp(path, &p, 0) == 0) {\n\t\tchar **w = p.we_wordv;\n\t\tsize_t i;\n\t\tfor (i = 0; i < p.we_wordc; ++i) {\n\t\t\tload_include_config(w[i], config, swaynag);\n\t\t}\n\t\twordfree(&p);\n\t}\n\n\t// Attempt to restore working directory before returning.\n\tif (chdir(wd) < 0) {\n\t\tsway_log(SWAY_ERROR, \"failed to change working directory\");\n\t}\ncleanup:\n\tfree(parent_path);\n\tfree(wd);\n}\n\nvoid run_deferred_commands(void) {\n\tif (!config->cmd_queue->length) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Running deferred commands\");\n\twhile (config->cmd_queue->length) {\n\t\tchar *line = config->cmd_queue->items[0];\n\t\tlist_t *res_list = execute_command(line, NULL, NULL);\n\t\tfor (int i = 0; i < res_list->length; ++i) {\n\t\t\tstruct cmd_results *res = res_list->items[i];\n\t\t\tif (res->status != CMD_SUCCESS) {\n\t\t\t\tsway_log(SWAY_ERROR, \"Error on line '%s': %s\",\n\t\t\t\t\t\tline, res->error);\n\t\t\t}\n\t\t\tfree_cmd_results(res);\n\t\t}\n\t\tlist_del(config->cmd_queue, 0);\n\t\tlist_free(res_list);\n\t\tfree(line);\n\t}\n}\n\nvoid run_deferred_bindings(void) {\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &(server.input->seats), link) {\n\t\tif (!seat->deferred_bindings->length) {\n\t\t\tcontinue;\n\t\t}\n\t\tsway_log(SWAY_DEBUG, \"Running deferred bindings for seat %s\",\n\t\t\t\tseat->wlr_seat->name);\n\t\twhile (seat->deferred_bindings->length) {\n\t\t\tstruct sway_binding *binding = seat->deferred_bindings->items[0];\n\t\t\tseat_execute_command(seat, binding);\n\t\t\tlist_del(seat->deferred_bindings, 0);\n\t\t\tfree_sway_binding(binding);\n\t\t}\n\t}\n}\n\n// get line, with backslash continuation\nstatic ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file,\n\t\tint *nlines) {\n\tchar *next_line = NULL;\n\tsize_t next_line_size = 0;\n\tssize_t nread = getline(lineptr, line_size, file);\n\t*nlines = nread == -1 ? 0 : 1;\n\twhile (nread >= 2 && strcmp(&(*lineptr)[nread - 2], \"\\\\\\n\") == 0 && (*lineptr)[0] != '#') {\n\t\tssize_t next_nread = getline(&next_line, &next_line_size, file);\n\t\tif (next_nread == -1) {\n\t\t\tbreak;\n\t\t}\n\t\t(*nlines)++;\n\n\t\tnread += next_nread - 2;\n\t\tif ((ssize_t) *line_size < nread + 1) {\n\t\t\t*line_size = nread + 1;\n\t\t\tchar *old_ptr = *lineptr;\n\t\t\t*lineptr = realloc(*lineptr, *line_size);\n\t\t\tif (!*lineptr) {\n\t\t\t\tfree(old_ptr);\n\t\t\t\tnread = -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tstrcpy(&(*lineptr)[nread - next_nread], next_line);\n\t}\n\tfree(next_line);\n\treturn nread;\n}\n\nstatic int detect_brace(FILE *file) {\n\tint ret = 0;\n\tint lines = 0;\n\tlong pos = ftell(file);\n\tchar *line = NULL;\n\tsize_t line_size = 0;\n\twhile ((getline(&line, &line_size, file)) != -1) {\n\t\tlines++;\n\t\tstrip_whitespace(line);\n\t\tif (*line) {\n\t\t\tif (strcmp(line, \"{\") == 0) {\n\t\t\t\tret = lines;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tfree(line);\n\tif (ret == 0) {\n\t\tfseek(file, pos, SEEK_SET);\n\t}\n\treturn ret;\n}\n\nstatic char *expand_line(const char *block, const char *line, bool add_brace) {\n\tint size = (block ? strlen(block) + 1 : 0) + strlen(line)\n\t\t+ (add_brace ? 2 : 0) + 1;\n\tchar *expanded = calloc(1, size);\n\tif (!expanded) {\n\t\tsway_log(SWAY_ERROR, \"Cannot allocate expanded line buffer\");\n\t\treturn NULL;\n\t}\n\tsnprintf(expanded, size, \"%s%s%s%s\", block ? block : \"\",\n\t\t\tblock ? \" \" : \"\", line, add_brace ? \" {\" : \"\");\n\treturn expanded;\n}\n\nbool read_config(FILE *file, struct sway_config *config,\n\t\tstruct swaynag_instance *swaynag) {\n\tbool reading_main_config = false;\n\tchar *this_config = NULL;\n\tsize_t config_size = 0;\n\tif (config->current_config == NULL) {\n\t\treading_main_config = true;\n\n\t\tint ret_seek = fseek(file, 0, SEEK_END);\n\t\tlong ret_tell = ftell(file);\n\t\tif (ret_seek == -1 || ret_tell == -1) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to get size of config file\");\n\t\t\treturn false;\n\t\t}\n\t\tconfig_size = ret_tell;\n\t\trewind(file);\n\n\t\tconfig->current_config = this_config = calloc(1, config_size + 1);\n\t\tif (this_config == NULL) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to allocate buffer for config contents\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tbool success = true;\n\tint line_number = 0;\n\tchar *line = NULL;\n\tsize_t line_size = 0;\n\tssize_t nread;\n\tlist_t *stack = create_list();\n\tsize_t read = 0;\n\tint nlines = 0;\n\twhile ((nread = getline_with_cont(&line, &line_size, file, &nlines)) != -1) {\n\t\tif (reading_main_config) {\n\t\t\tif (read + nread > config_size) {\n\t\t\t\tsway_log(SWAY_ERROR, \"Config file changed during reading\");\n\t\t\t\tsuccess = false;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tstrcpy(&this_config[read], line);\n\t\t\tread += nread;\n\t\t}\n\n\t\tif (line[nread - 1] == '\\n') {\n\t\t\tline[nread - 1] = '\\0';\n\t\t}\n\n\t\tline_number += nlines;\n\t\tsway_log(SWAY_DEBUG, \"Read line %d: %s\", line_number, line);\n\n\t\tstrip_whitespace(line);\n\t\tif (!*line || line[0] == '#') {\n\t\t\tcontinue;\n\t\t}\n\t\tint brace_detected = 0;\n\t\tif (line[strlen(line) - 1] != '{' && line[strlen(line) - 1] != '}') {\n\t\t\tbrace_detected = detect_brace(file);\n\t\t\tif (brace_detected > 0) {\n\t\t\t\tline_number += brace_detected;\n\t\t\t\tsway_log(SWAY_DEBUG, \"Detected open brace on line %d\", line_number);\n\t\t\t}\n\t\t}\n\t\tchar *block = stack->length ? stack->items[0] : NULL;\n\t\tchar *expanded = expand_line(block, line, brace_detected > 0);\n\t\tif (!expanded) {\n\t\t\tsuccess = false;\n\t\t\tbreak;\n\t\t}\n\t\tconfig->current_config_line_number = line_number;\n\t\tconfig->current_config_line = line;\n\t\tstruct cmd_results *res;\n\t\tchar *new_block = NULL;\n\t\tif (block && strcmp(block, \"<commands>\") == 0) {\n\t\t\t// Special case\n\t\t\tres = config_commands_command(expanded);\n\t\t} else {\n\t\t\tres = config_command(expanded, &new_block);\n\t\t}\n\t\tswitch(res->status) {\n\t\tcase CMD_FAILURE:\n\t\tcase CMD_INVALID:\n\t\t\tsway_log(SWAY_ERROR, \"Error on line %i '%s': %s (%s)\", line_number,\n\t\t\t\tline, res->error, config->current_config_path);\n\t\t\tif (!config->validating) {\n\t\t\t\tswaynag_log(config->swaynag_command, swaynag,\n\t\t\t\t\t\"Error on line %i (%s) '%s': %s\", line_number,\n\t\t\t\t\tconfig->current_config_path, line, res->error);\n\t\t\t}\n\t\t\tsuccess = false;\n\t\t\tbreak;\n\n\t\tcase CMD_DEFER:\n\t\t\tsway_log(SWAY_DEBUG, \"Deferring command `%s'\", line);\n\t\t\tlist_add(config->cmd_queue, strdup(expanded));\n\t\t\tbreak;\n\n\t\tcase CMD_BLOCK_COMMANDS:\n\t\t\tsway_log(SWAY_DEBUG, \"Entering commands block\");\n\t\t\tlist_insert(stack, 0, \"<commands>\");\n\t\t\tbreak;\n\n\t\tcase CMD_BLOCK:\n\t\t\tsway_log(SWAY_DEBUG, \"Entering block '%s'\", new_block);\n\t\t\tlist_insert(stack, 0, strdup(new_block));\n\t\t\tif (strcmp(new_block, \"bar\") == 0) {\n\t\t\t\tconfig->current_bar = NULL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase CMD_BLOCK_END:\n\t\t\tif (!block) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Unmatched '}' on line %i\", line_number);\n\t\t\t\tsuccess = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (strcmp(block, \"bar\") == 0) {\n\t\t\t\tconfig->current_bar = NULL;\n\t\t\t}\n\n\t\t\tsway_log(SWAY_DEBUG, \"Exiting block '%s'\", block);\n\t\t\tlist_del(stack, 0);\n\t\t\tfree(block);\n\t\t\tmemset(&config->handler_context, 0,\n\t\t\t\t\tsizeof(config->handler_context));\n\t\tdefault:;\n\t\t}\n\t\tfree(new_block);\n\t\tfree(expanded);\n\t\tfree_cmd_results(res);\n\t}\n\tfree(line);\n\tlist_free_items_and_destroy(stack);\n\tconfig->current_config_line_number = 0;\n\tconfig->current_config_line = NULL;\n\n\treturn success;\n}\n\nvoid config_add_swaynag_warning(char *fmt, ...) {\n\tif (config->reading && !config->validating) {\n\t\tva_list args;\n\t\tva_start(args, fmt);\n\t\tchar *str = vformat_str(fmt, args);\n\t\tva_end(args);\n\t\tif (str == NULL) {\n\t\t\treturn;\n\t\t}\n\n\t\tswaynag_log(config->swaynag_command, &config->swaynag_config_errors,\n\t\t\t\"Warning on line %i (%s) '%s': %s\",\n\t\t\tconfig->current_config_line_number, config->current_config_path,\n\t\t\tconfig->current_config_line, str);\n\n\t\tfree(str);\n\t}\n}\n\nchar *do_var_replacement(char *str) {\n\tint i;\n\tchar *find = str;\n\twhile ((find = strchr(find, '$'))) {\n\t\t// Skip if escaped.\n\t\tif (find > str && find[-1] == '\\\\') {\n\t\t\tif (find == str + 1 || !(find > str + 1 && find[-2] == '\\\\')) {\n\t\t\t\t++find;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t// Unescape double $ and move on\n\t\tif (find[1] == '$') {\n\t\t\tsize_t length = strlen(find + 1);\n\t\t\tmemmove(find, find + 1, length);\n\t\t\tfind[length] = '\\0';\n\t\t\t++find;\n\t\t\tcontinue;\n\t\t}\n\t\t// Find matching variable\n\t\tfor (i = 0; i < config->symbols->length; ++i) {\n\t\t\tstruct sway_variable *var = config->symbols->items[i];\n\t\t\tif (has_prefix(find, var->name)) {\n\t\t\t\tint vnlen = strlen(var->name);\n\t\t\t\tint vvlen = strlen(var->value);\n\t\t\t\tchar *newstr = malloc(strlen(str) - vnlen + vvlen + 1);\n\t\t\t\tif (!newstr) {\n\t\t\t\t\tsway_log(SWAY_ERROR,\n\t\t\t\t\t\t\"Unable to allocate replacement \"\n\t\t\t\t\t\t\"during variable expansion\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tchar *newptr = newstr;\n\t\t\t\tint offset = find - str;\n\t\t\t\tstrncpy(newptr, str, offset);\n\t\t\t\tnewptr += offset;\n\t\t\t\tmemcpy(newptr, var->value, vvlen);\n\t\t\t\tnewptr += vvlen;\n\t\t\t\tstrcpy(newptr, find + vnlen);\n\t\t\t\tfree(str);\n\t\t\t\tstr = newstr;\n\t\t\t\tfind = str + offset + vvlen;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == config->symbols->length) {\n\t\t\t++find;\n\t\t}\n\t}\n\treturn str;\n}\n\n// the naming is intentional (albeit long): a workspace_output_cmp function\n// would compare two structs in full, while this method only compares the\n// workspace.\nint workspace_output_cmp_workspace(const void *a, const void *b) {\n\tconst struct workspace_config *wsa = a, *wsb = b;\n\treturn lenient_strcmp(wsa->workspace, wsb->workspace);\n}\n\n\nvoid config_update_font_height(void) {\n\tint prev_max_height = config->font_height;\n\n\tget_text_metrics(config->font_description, &config->font_height, &config->font_baseline);\n\n\tif (config->font_height != prev_max_height) {\n\t\tarrange_root();\n\t}\n}\n\nstatic void translate_binding_list(list_t *bindings, list_t *bindsyms,\n\t\tlist_t *bindcodes) {\n\tfor (int i = 0; i < bindings->length; ++i) {\n\t\tstruct sway_binding *binding = bindings->items[i];\n\t\ttranslate_binding(binding);\n\n\t\tswitch (binding->type) {\n\t\tcase BINDING_KEYSYM:\n\t\t\tbinding_add_translated(binding, bindsyms);\n\t\t\tbreak;\n\t\tcase BINDING_KEYCODE:\n\t\t\tbinding_add_translated(binding, bindcodes);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tsway_assert(false, \"unexpected translated binding type: %d\",\n\t\t\t\t\tbinding->type);\n\t\t\tbreak;\n\t\t}\n\n\t}\n}\n\nvoid translate_keysyms(struct input_config *input_config) {\n\tkeysym_translation_state_destroy(config->keysym_translation_state);\n\n\tstruct xkb_rule_names rules = {0};\n\tinput_config_fill_rule_names(input_config, &rules);\n\tconfig->keysym_translation_state = keysym_translation_state_create(rules, 0);\n\tif (config->keysym_translation_state == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create keysym translation XKB state \"\n\t\t\t\"for device '%s'\", input_config->identifier);\n\t\treturn;\n\t}\n\n\tfor (int i = 0; i < config->modes->length; ++i) {\n\t\tstruct sway_mode *mode = config->modes->items[i];\n\n\t\tlist_t *bindsyms = create_list();\n\t\tlist_t *bindcodes = create_list();\n\n\t\ttranslate_binding_list(mode->keysym_bindings, bindsyms, bindcodes);\n\t\ttranslate_binding_list(mode->keycode_bindings, bindsyms, bindcodes);\n\n\t\tlist_free(mode->keysym_bindings);\n\t\tlist_free(mode->keycode_bindings);\n\n\t\tmode->keysym_bindings = bindsyms;\n\t\tmode->keycode_bindings = bindcodes;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Translated keysyms using config for device '%s'\",\n\t\t\tinput_config->identifier);\n}\n"
  },
  {
    "path": "sway/criteria.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include <strings.h>\n#define PCRE2_CODE_UNIT_WIDTH 8\n#include <pcre2.h>\n#include \"sway/criteria.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/config.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"stringop.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"config.h\"\n\nbool criteria_is_empty(struct criteria *criteria) {\n\treturn !criteria->title\n\t\t&& !criteria->shell\n\t\t&& !criteria->all\n\t\t&& !criteria->app_id\n\t\t&& !criteria->con_mark\n\t\t&& !criteria->con_id\n#if WLR_HAS_XWAYLAND\n\t\t&& !criteria->class\n\t\t&& !criteria->id\n\t\t&& !criteria->instance\n\t\t&& !criteria->window_role\n\t\t&& criteria->window_type == ATOM_LAST\n#endif\n\t\t&& !criteria->floating\n\t\t&& !criteria->tiling\n\t\t&& !criteria->urgent\n\t\t&& !criteria->workspace\n\t\t&& !criteria->pid\n\t\t&& !criteria->sandbox_engine\n\t\t&& !criteria->sandbox_app_id\n\t\t&& !criteria->sandbox_instance_id\n\t\t&& !criteria->tag;\n}\n\n// The error pointer is used for parsing functions, and saves having to pass it\n// as an argument in several places.\nchar *error = NULL;\n\n// Returns error string on failure or NULL otherwise.\nstatic bool generate_regex(pcre2_code **regex, char *value) {\n\tint errorcode;\n\tPCRE2_SIZE offset;\n\n\t*regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL);\n\tif (!*regex) {\n\t\tPCRE2_UCHAR buffer[256];\n\t\tpcre2_get_error_message(errorcode, buffer, sizeof(buffer));\n\n\t\tconst char *fmt = \"Regex compilation for '%s' failed: %s\";\n\t\tint len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3;\n\t\terror = malloc(len);\n\t\tsnprintf(error, len, fmt, value, buffer);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic bool pattern_create(struct pattern **pattern, char *value) {\n\t*pattern = calloc(1, sizeof(struct pattern));\n\tif (!*pattern) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate pattern\");\n\t}\n\n\tif (strcmp(value, \"__focused__\") == 0) {\n\t\t(*pattern)->match_type = PATTERN_FOCUSED;\n\t} else {\n\t\t(*pattern)->match_type = PATTERN_PCRE2;\n\t\tif (!generate_regex(&(*pattern)->regex, value)) {\n\t\t\treturn false;\n\t\t};\n\t}\n\treturn true;\n}\n\nstatic void pattern_destroy(struct pattern *pattern) {\n\tif (pattern) {\n\t\tif (pattern->regex) {\n\t\t\tpcre2_code_free(pattern->regex);\n\t\t}\n\t\tfree(pattern);\n\t}\n}\n\nvoid criteria_destroy(struct criteria *criteria) {\n\tpattern_destroy(criteria->title);\n\tpattern_destroy(criteria->shell);\n\tpattern_destroy(criteria->app_id);\n#if WLR_HAS_XWAYLAND\n\tpattern_destroy(criteria->class);\n\tpattern_destroy(criteria->instance);\n\tpattern_destroy(criteria->window_role);\n#endif\n\tpattern_destroy(criteria->con_mark);\n\tpattern_destroy(criteria->workspace);\n\tpattern_destroy(criteria->sandbox_engine);\n\tpattern_destroy(criteria->sandbox_app_id);\n\tpattern_destroy(criteria->sandbox_instance_id);\n\tpattern_destroy(criteria->tag);\n\tfree(criteria->target);\n\tfree(criteria->cmdlist);\n\tfree(criteria->raw);\n\tfree(criteria);\n}\n\nstatic int regex_cmp(const char *item, const pcre2_code *regex) {\n\tpcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);\n\tint result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL);\n\tpcre2_match_data_free(match_data);\n\treturn result;\n}\n\n#if WLR_HAS_XWAYLAND\nstatic bool view_has_window_type(struct sway_view *view, enum atom_name name) {\n\tif (view->type != SWAY_VIEW_XWAYLAND) {\n\t\treturn false;\n\t}\n\tstruct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;\n\tstruct sway_xwayland *xwayland = &server.xwayland;\n\txcb_atom_t desired_atom = xwayland->atoms[name];\n\tfor (size_t i = 0; i < surface->window_type_len; ++i) {\n\t\tif (surface->window_type[i] == desired_atom) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n#endif\n\nstatic int cmp_urgent(const void *_a, const void *_b) {\n\tstruct sway_view *a = *(void **)_a;\n\tstruct sway_view *b = *(void **)_b;\n\n\tif (a->urgent.tv_sec < b->urgent.tv_sec) {\n\t\treturn -1;\n\t} else if (a->urgent.tv_sec > b->urgent.tv_sec) {\n\t\treturn 1;\n\t}\n\tif (a->urgent.tv_nsec < b->urgent.tv_nsec) {\n\t\treturn -1;\n\t} else if (a->urgent.tv_nsec > b->urgent.tv_nsec) {\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic void find_urgent_iterator(struct sway_container *con, void *data) {\n\tif (!con->view || !view_is_urgent(con->view)) {\n\t\treturn;\n\t}\n\tlist_t *urgent_views = data;\n\tlist_add(urgent_views, con->view);\n}\n\nstatic bool has_container_criteria(struct criteria *criteria) {\n\treturn criteria->con_mark || criteria->con_id;\n}\n\nstatic bool criteria_matches_container(struct criteria *criteria,\n\t\tstruct sway_container *container) {\n\tif (criteria->con_mark) {\n\t\tbool exists = false;\n\t\tstruct sway_container *con = container;\n\t\tfor (int i = 0; i < con->marks->length; ++i) {\n\t\t\tif (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) {\n\t\t\t\texists = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!exists) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (criteria->con_id) { // Internal ID\n\t\tif (container->node.id != criteria->con_id) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic bool criteria_matches_view(struct criteria *criteria,\n\t\tstruct sway_view *view) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_container *focus = seat_get_focused_container(seat);\n\tstruct sway_view *focused = focus ? focus->view : NULL;\n\n\tif (!view->container) {\n\t\treturn false;\n\t}\n\n\tif (criteria->title) {\n\t\tconst char *title = view_get_title(view);\n\t\tif (!title) {\n\t\t\ttitle = \"\";\n\t\t}\n\n\t\tswitch (criteria->title->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(title, view_get_title(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(title, criteria->title->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->shell) {\n\t\tconst char *shell = view_get_shell(view);\n\t\tif (!shell) {\n\t\t\tshell = \"\";\n\t\t}\n\n\t\tswitch (criteria->shell->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || strcmp(shell, view_get_shell(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(shell, criteria->shell->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->app_id) {\n\t\tconst char *app_id = view_get_app_id(view);\n\t\tif (!app_id) {\n\t\t\tapp_id = \"\";\n\t\t}\n\n\t\tswitch (criteria->app_id->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(app_id, view_get_app_id(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(app_id, criteria->app_id->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->sandbox_engine) {\n\t\tconst char *sandbox_engine = view_get_sandbox_engine(view);\n\t\tif (!sandbox_engine) {\n\t\t\treturn false;\n\t\t}\n\n\t\tswitch (criteria->sandbox_engine->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(sandbox_engine, view_get_sandbox_engine(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(sandbox_engine, criteria->sandbox_engine->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->sandbox_app_id) {\n\t\tconst char *sandbox_app_id = view_get_sandbox_app_id(view);\n\t\tif (!sandbox_app_id) {\n\t\t\treturn false;\n\t\t}\n\n\t\tswitch (criteria->sandbox_app_id->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(sandbox_app_id, view_get_sandbox_app_id(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(sandbox_app_id, criteria->sandbox_app_id->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->sandbox_instance_id) {\n\t\tconst char *sandbox_instance_id = view_get_sandbox_instance_id(view);\n\t\tif (!sandbox_instance_id) {\n\t\t\treturn false;\n\t\t}\n\n\t\tswitch (criteria->sandbox_instance_id->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(sandbox_instance_id, view_get_sandbox_instance_id(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(sandbox_instance_id, criteria->sandbox_instance_id->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->tag) {\n\t\tconst char *tag = view_get_tag(view);\n\t\tif (!tag) {\n\t\t\treturn false;\n\t\t}\n\n\t\tswitch (criteria->tag->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(tag, view_get_tag(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(tag, criteria->tag->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!criteria_matches_container(criteria, view->container)) {\n\t\treturn false;\n\t}\n\n#if WLR_HAS_XWAYLAND\n\tif (criteria->id) { // X11 window ID\n\t\tuint32_t x11_window_id = view_get_x11_window_id(view);\n\t\tif (!x11_window_id || x11_window_id != criteria->id) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (criteria->class) {\n\t\tconst char *class = view_get_class(view);\n\t\tif (!class) {\n\t\t\tclass = \"\";\n\t\t}\n\n\t\tswitch (criteria->class->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(class, view_get_class(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(class, criteria->class->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->instance) {\n\t\tconst char *instance = view_get_instance(view);\n\t\tif (!instance) {\n\t\t\tinstance = \"\";\n\t\t}\n\n\t\tswitch (criteria->instance->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(instance, view_get_instance(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(instance, criteria->instance->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->window_role) {\n\t\tconst char *window_role = view_get_window_role(view);\n\t\tif (!window_role) {\n\t\t\twindow_role = \"\";\n\t\t}\n\n\t\tswitch (criteria->window_role->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused || lenient_strcmp(window_role, view_get_window_role(focused))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(window_role, criteria->window_role->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->window_type != ATOM_LAST) {\n\t\tif (!view_has_window_type(view, criteria->window_type)) {\n\t\t\treturn false;\n\t\t}\n\t}\n#endif\n\n\tif (criteria->floating) {\n\t\tif (!container_is_floating(view->container)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (criteria->tiling) {\n\t\tif (container_is_floating(view->container)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (criteria->urgent) {\n\t\tif (!view_is_urgent(view)) {\n\t\t\treturn false;\n\t\t}\n\t\tlist_t *urgent_views = create_list();\n\t\troot_for_each_container(find_urgent_iterator, urgent_views);\n\t\tlist_stable_sort(urgent_views, cmp_urgent);\n\t\tstruct sway_view *target;\n\t\tif (criteria->urgent == 'o') { // oldest\n\t\t\ttarget = urgent_views->items[0];\n\t\t} else { // latest\n\t\t\ttarget = urgent_views->items[urgent_views->length - 1];\n\t\t}\n\t\tlist_free(urgent_views);\n\t\tif (view != target) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (criteria->workspace) {\n\t\tstruct sway_workspace *ws = view->container->pending.workspace;\n\t\tif (!ws) {\n\t\t\treturn false;\n\t\t}\n\n\t\tswitch (criteria->workspace->match_type) {\n\t\tcase PATTERN_FOCUSED:\n\t\t\tif (!focused ||\n\t\t\t\t\tstrcmp(ws->name, focused->container->pending.workspace->name)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PATTERN_PCRE2:\n\t\t\tif (regex_cmp(ws->name, criteria->workspace->regex) < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (criteria->pid) {\n\t\tif (criteria->pid != view->pid) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nlist_t *criteria_for_view(struct sway_view *view, enum criteria_type types) {\n\tlist_t *criterias = config->criteria;\n\tlist_t *matches = create_list();\n\tfor (int i = 0; i < criterias->length; ++i) {\n\t\tstruct criteria *criteria = criterias->items[i];\n\t\tif ((criteria->type & types) && criteria_matches_view(criteria, view)) {\n\t\t\tlist_add(matches, criteria);\n\t\t}\n\t}\n\treturn matches;\n}\n\nstruct match_data {\n\tstruct criteria *criteria;\n\tlist_t *matches;\n};\n\nstatic void criteria_get_containers_iterator(struct sway_container *container,\n\t\tvoid *data) {\n\tstruct match_data *match_data = data;\n\tif (container->view) {\n\t\tif (criteria_matches_view(match_data->criteria, container->view)) {\n\t\t\tlist_add(match_data->matches, container);\n\t\t}\n\t} else if (has_container_criteria(match_data->criteria)) {\n\t\tif (criteria_matches_container(match_data->criteria, container)) {\n\t\t\tlist_add(match_data->matches, container);\n\t\t}\n\t}\n}\n\nlist_t *criteria_get_containers(struct criteria *criteria) {\n\tlist_t *matches = create_list();\n\tstruct match_data data = {\n\t\t.criteria = criteria,\n\t\t.matches = matches,\n\t};\n\troot_for_each_container(criteria_get_containers_iterator, &data);\n\treturn matches;\n}\n\n#if WLR_HAS_XWAYLAND\nstatic enum atom_name parse_window_type(const char *type) {\n\tif (strcasecmp(type, \"normal\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_NORMAL;\n\t} else if (strcasecmp(type, \"dialog\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_DIALOG;\n\t} else if (strcasecmp(type, \"utility\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_UTILITY;\n\t} else if (strcasecmp(type, \"toolbar\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_TOOLBAR;\n\t} else if (strcasecmp(type, \"splash\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_SPLASH;\n\t} else if (strcasecmp(type, \"menu\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_MENU;\n\t} else if (strcasecmp(type, \"dropdown_menu\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_DROPDOWN_MENU;\n\t} else if (strcasecmp(type, \"popup_menu\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_POPUP_MENU;\n\t} else if (strcasecmp(type, \"tooltip\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_TOOLTIP;\n\t} else if (strcasecmp(type, \"notification\") == 0) {\n\t\treturn NET_WM_WINDOW_TYPE_NOTIFICATION;\n\t}\n\treturn ATOM_LAST; // ie. invalid\n}\n#endif\n\nenum criteria_token {\n\tT_ALL,\n\tT_APP_ID,\n\tT_CON_ID,\n\tT_CON_MARK,\n\tT_FLOATING,\n#if WLR_HAS_XWAYLAND\n\tT_CLASS,\n\tT_ID,\n\tT_INSTANCE,\n\tT_WINDOW_ROLE,\n\tT_WINDOW_TYPE,\n#endif\n\tT_SHELL,\n\tT_TILING,\n\tT_TITLE,\n\tT_URGENT,\n\tT_WORKSPACE,\n\tT_PID,\n\tT_SANDBOX_ENGINE,\n\tT_SANDBOX_APP_ID,\n\tT_SANDBOX_INSTANCE_ID,\n\tT_TAG,\n\n\tT_INVALID,\n};\n\nstatic enum criteria_token token_from_name(char *name) {\n\tif (strcmp(name, \"all\") == 0) {\n\t\treturn T_ALL;\n\t} else if (strcmp(name, \"app_id\") == 0) {\n\t\treturn T_APP_ID;\n\t} else if (strcmp(name, \"con_id\") == 0) {\n\t\treturn T_CON_ID;\n\t} else if (strcmp(name, \"con_mark\") == 0) {\n\t\treturn T_CON_MARK;\n#if WLR_HAS_XWAYLAND\n\t} else if (strcmp(name, \"class\") == 0) {\n\t\treturn T_CLASS;\n\t} else if (strcmp(name, \"id\") == 0) {\n\t\treturn T_ID;\n\t} else if (strcmp(name, \"instance\") == 0) {\n\t\treturn T_INSTANCE;\n\t} else if (strcmp(name, \"window_role\") == 0) {\n\t\treturn T_WINDOW_ROLE;\n\t} else if (strcmp(name, \"window_type\") == 0) {\n\t\treturn T_WINDOW_TYPE;\n#endif\n\t} else if (strcmp(name, \"shell\") == 0) {\n\t\treturn T_SHELL;\n\t} else if (strcmp(name, \"title\") == 0) {\n\t\treturn T_TITLE;\n\t} else if (strcmp(name, \"urgent\") == 0) {\n\t\treturn T_URGENT;\n\t} else if (strcmp(name, \"workspace\") == 0) {\n\t\treturn T_WORKSPACE;\n\t} else if (strcmp(name, \"tiling\") == 0) {\n\t\treturn T_TILING;\n\t} else if (strcmp(name, \"floating\") == 0) {\n\t\treturn T_FLOATING;\n\t} else if (strcmp(name, \"pid\") == 0) {\n\t\treturn T_PID;\n\t} else if (strcmp(name, \"sandbox_engine\") == 0) {\n\t\treturn T_SANDBOX_ENGINE;\n\t} else if (strcmp(name, \"sandbox_app_id\") == 0) {\n\t\treturn T_SANDBOX_APP_ID;\n\t} else if (strcmp(name, \"sandbox_instance_id\") == 0) {\n\t\treturn T_SANDBOX_INSTANCE_ID;\n\t} else if (strcmp(name, \"tag\") == 0) {\n\t\treturn T_TAG;\n\t}\n\treturn T_INVALID;\n}\n\nstatic bool parse_token(struct criteria *criteria, char *name, char *value) {\n\tenum criteria_token token = token_from_name(name);\n\tif (token == T_INVALID) {\n\t\tconst char *fmt = \"Token '%s' is not recognized\";\n\t\tint len = strlen(fmt) + strlen(name) - 1;\n\t\terror = malloc(len);\n\t\tsnprintf(error, len, fmt, name);\n\t\treturn false;\n\t}\n\n\t// Require value, unless token is all, floating or tiled\n\tif (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) {\n\t\tconst char *fmt = \"Token '%s' requires a value\";\n\t\tint len = strlen(fmt) + strlen(name) - 1;\n\t\terror = malloc(len);\n\t\tsnprintf(error, len, fmt, name);\n\t\treturn false;\n\t}\n\n\tchar *endptr = NULL;\n\tswitch (token) {\n\tcase T_ALL:\n\t\tcriteria->all = true;\n\t\tbreak;\n\tcase T_TITLE:\n\t\tpattern_create(&criteria->title, value);\n\t\tbreak;\n\tcase T_SHELL:\n\t\tpattern_create(&criteria->shell, value);\n\t\tbreak;\n\tcase T_APP_ID:\n\t\tpattern_create(&criteria->app_id, value);\n\t\tbreak;\n\tcase T_CON_ID:\n\t\tif (strcmp(value, \"__focused__\") == 0) {\n\t\t\tstruct sway_seat *seat = input_manager_current_seat();\n\t\t\tstruct sway_container *focus = seat_get_focused_container(seat);\n\t\t\tcriteria->con_id = focus ? focus->node.id : 0;\n\t\t} else {\n\t\t\tcriteria->con_id = strtoul(value, &endptr, 10);\n\t\t\tif (*endptr != 0) {\n\t\t\t\terror = strdup(\"The value for 'con_id' should be '__focused__' or numeric\");\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase T_CON_MARK:\n\t\tpattern_create(&criteria->con_mark, value);\n\t\tbreak;\n#if WLR_HAS_XWAYLAND\n\tcase T_CLASS:\n\t\tpattern_create(&criteria->class, value);\n\t\tbreak;\n\tcase T_ID:\n\t\tcriteria->id = strtoul(value, &endptr, 10);\n\t\tif (*endptr != 0) {\n\t\t\terror = strdup(\"The value for 'id' should be numeric\");\n\t\t}\n\t\tbreak;\n\tcase T_INSTANCE:\n\t\tpattern_create(&criteria->instance, value);\n\t\tbreak;\n\tcase T_WINDOW_ROLE:\n\t\tpattern_create(&criteria->window_role, value);\n\t\tbreak;\n\tcase T_WINDOW_TYPE:\n\t\tcriteria->window_type = parse_window_type(value);\n\t\tbreak;\n#endif\n\tcase T_FLOATING:\n\t\tcriteria->floating = true;\n\t\tbreak;\n\tcase T_TILING:\n\t\tcriteria->tiling = true;\n\t\tbreak;\n\tcase T_URGENT:\n\t\tif (strcmp(value, \"latest\") == 0 ||\n\t\t\t\tstrcmp(value, \"newest\") == 0 ||\n\t\t\t\tstrcmp(value, \"last\") == 0 ||\n\t\t\t\tstrcmp(value, \"recent\") == 0) {\n\t\t\tcriteria->urgent = 'l';\n\t\t} else if (strcmp(value, \"oldest\") == 0 ||\n\t\t\t\tstrcmp(value, \"first\") == 0) {\n\t\t\tcriteria->urgent = 'o';\n\t\t} else {\n\t\t\terror =\n\t\t\t\tstrdup(\"The value for 'urgent' must be 'first', 'last', \"\n\t\t\t\t\t\t\"'latest', 'newest', 'oldest' or 'recent'\");\n\t\t}\n\t\tbreak;\n\tcase T_WORKSPACE:\n\t\tpattern_create(&criteria->workspace, value);\n\t\tbreak;\n\tcase T_PID:\n\t\tcriteria->pid = strtoul(value, &endptr, 10);\n\t\tif (*endptr != 0) {\n\t\t\terror = strdup(\"The value for 'pid' should be numeric\");\n\t\t}\n\t\tbreak;\n\tcase T_SANDBOX_ENGINE:\n\t\tpattern_create(&criteria->sandbox_engine, value);\n\t\tbreak;\n\tcase T_SANDBOX_APP_ID:\n\t\tpattern_create(&criteria->sandbox_app_id, value);\n\t\tbreak;\n\tcase T_SANDBOX_INSTANCE_ID:\n\t\tpattern_create(&criteria->sandbox_instance_id, value);\n\t\tbreak;\n\tcase T_TAG:\n\t\tpattern_create(&criteria->tag, value);\n\t\tbreak;\n\tcase T_INVALID:\n\t\tbreak;\n\t}\n\n\tif (error) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic void skip_spaces(char **head) {\n\twhile (**head == ' ') {\n\t\t++*head;\n\t}\n}\n\n// Remove escaping slashes from value\nstatic void unescape(char *value) {\n\tif (!strchr(value, '\\\\')) {\n\t\treturn;\n\t}\n\tchar *copy = calloc(strlen(value) + 1, 1);\n\tchar *readhead = value;\n\tchar *writehead = copy;\n\twhile (*readhead) {\n\t\tif (*readhead == '\\\\' && *(readhead + 1) == '\"') {\n\t\t\t// skip the slash\n\t\t\t++readhead;\n\t\t}\n\t\t*writehead = *readhead;\n\t\t++writehead;\n\t\t++readhead;\n\t}\n\tstrcpy(value, copy);\n\tfree(copy);\n}\n\n/**\n * Parse a raw criteria string such as [class=\"foo\" instance=\"bar\"] into a\n * criteria struct.\n *\n * If errors are found, NULL will be returned and the error argument will be\n * populated with an error string. It is up to the caller to free the error.\n */\nstruct criteria *criteria_parse(char *raw, char **error_arg) {\n\t*error_arg = NULL;\n\terror = NULL;\n\n\tchar *head = raw;\n\tskip_spaces(&head);\n\tif (*head != '[') {\n\t\t*error_arg = strdup(\"No criteria\");\n\t\treturn NULL;\n\t}\n\t++head;\n\n\tstruct criteria *criteria = calloc(1, sizeof(struct criteria));\n#if WLR_HAS_XWAYLAND\n\tcriteria->window_type = ATOM_LAST; // default value\n#endif\n\tchar *name = NULL, *value = NULL;\n\tbool in_quotes = false;\n\n\twhile (*head && *head != ']') {\n\t\tskip_spaces(&head);\n\t\t// Parse token name\n\t\tchar *namestart = head;\n\t\twhile ((*head >= 'a' && *head <= 'z') || *head == '_') {\n\t\t\t++head;\n\t\t}\n\t\tname = calloc(head - namestart + 1, 1);\n\t\tif (head != namestart) {\n\t\t\tmemcpy(name, namestart, head - namestart);\n\t\t}\n\t\t// Parse token value\n\t\tskip_spaces(&head);\n\t\tvalue = NULL;\n\t\tif (*head == '=') {\n\t\t\t++head;\n\t\t\tskip_spaces(&head);\n\t\t\tif (*head == '\"') {\n\t\t\t\tin_quotes = true;\n\t\t\t\t++head;\n\t\t\t}\n\t\t\tchar *valuestart = head;\n\t\t\tif (in_quotes) {\n\t\t\t\twhile (*head && (*head != '\"' || *(head - 1) == '\\\\')) {\n\t\t\t\t\t++head;\n\t\t\t\t}\n\t\t\t\tif (!*head) {\n\t\t\t\t\t*error_arg = strdup(\"Quote mismatch in criteria\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile (*head && *head != ' ' && *head != ']') {\n\t\t\t\t\t++head;\n\t\t\t\t}\n\t\t\t}\n\t\t\tvalue = calloc(head - valuestart + 1, 1);\n\t\t\tmemcpy(value, valuestart, head - valuestart);\n\t\t\tif (in_quotes) {\n\t\t\t\t++head;\n\t\t\t\tin_quotes = false;\n\t\t\t}\n\t\t\tunescape(value);\n\t\t\tsway_log(SWAY_DEBUG, \"Found pair: %s=%s\", name, value);\n\t\t}\n\t\tif (!parse_token(criteria, name, value)) {\n\t\t\t*error_arg = error;\n\t\t\tgoto cleanup;\n\t\t}\n\t\tskip_spaces(&head);\n\t\tfree(name);\n\t\tfree(value);\n\t\tname = NULL;\n\t\tvalue = NULL;\n\t}\n\tif (*head != ']') {\n\t\t*error_arg = strdup(\"No closing brace found in criteria\");\n\t\tgoto cleanup;\n\t}\n\n\tif (criteria_is_empty(criteria)) {\n\t\t*error_arg = strdup(\"Criteria is empty\");\n\t\tgoto cleanup;\n\t}\n\n\t++head;\n\tint len = head - raw;\n\tcriteria->raw = calloc(len + 1, 1);\n\tmemcpy(criteria->raw, raw, len);\n\treturn criteria;\n\ncleanup:\n\tfree(name);\n\tfree(value);\n\tcriteria_destroy(criteria);\n\treturn NULL;\n}\n\nbool criteria_is_equal(struct criteria *left, struct criteria *right) {\n\tif (left->type != right->type) {\n\t\treturn false;\n\t}\n\t// XXX Only implemented for CT_NO_FOCUS for now.\n\tif (left->type == CT_NO_FOCUS) {\n\t\treturn strcmp(left->raw, right->raw) == 0;\n\t}\n\tif (left->type == CT_COMMAND) {\n\t\treturn strcmp(left->raw, right->raw) == 0\n\t\t\t\t&& strcmp(left->cmdlist, right->cmdlist) == 0;\n\t}\n\treturn false;\n}\n\nbool criteria_already_exists(struct criteria *criteria) {\n\t// XXX Only implemented for CT_NO_FOCUS and CT_COMMAND for now.\n\t// While criteria_is_equal also obeys this limitation, this is a shortcut\n\t// to avoid processing the list.\n\tif (criteria->type != CT_NO_FOCUS && criteria->type != CT_COMMAND) {\n\t\treturn false;\n\t}\n\n\tlist_t *criterias = config->criteria;\n\tfor (int i = 0; i < criterias->length; ++i) {\n\t\tstruct criteria *existing = criterias->items[i];\n\t\tif (criteria_is_equal(criteria, existing)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "sway/decoration.c",
    "content": "#include <stdlib.h>\n#include \"sway/decoration.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"log.h\"\n\nstatic void server_decoration_handle_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_server_decoration *deco =\n\t\twl_container_of(listener, deco, destroy);\n\twl_list_remove(&deco->destroy.link);\n\twl_list_remove(&deco->mode.link);\n\twl_list_remove(&deco->link);\n\tfree(deco);\n}\n\nstatic void server_decoration_handle_mode(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_server_decoration *deco =\n\t\twl_container_of(listener, deco, mode);\n\tstruct sway_view *view =\n\t\tview_from_wlr_surface(deco->wlr_server_decoration->surface);\n\tif (view == NULL || view->surface != deco->wlr_server_decoration->surface) {\n\t\treturn;\n\t}\n\n\tbool csd = deco->wlr_server_decoration->mode ==\n\t\t\tWLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;\n\tview_update_csd_from_client(view, csd);\n\n\tarrange_container(view->container);\n\ttransaction_commit_dirty();\n}\n\nvoid handle_server_decoration(struct wl_listener *listener, void *data) {\n\tstruct wlr_server_decoration *wlr_deco = data;\n\n\tstruct sway_server_decoration *deco = calloc(1, sizeof(*deco));\n\tif (deco == NULL) {\n\t\treturn;\n\t}\n\n\tdeco->wlr_server_decoration = wlr_deco;\n\n\twl_signal_add(&wlr_deco->events.destroy, &deco->destroy);\n\tdeco->destroy.notify = server_decoration_handle_destroy;\n\n\twl_signal_add(&wlr_deco->events.mode, &deco->mode);\n\tdeco->mode.notify = server_decoration_handle_mode;\n\n\twl_list_insert(&server.decorations, &deco->link);\n}\n\nstruct sway_server_decoration *decoration_from_surface(\n\t\tstruct wlr_surface *surface) {\n\tstruct sway_server_decoration *deco;\n\twl_list_for_each(deco, &server.decorations, link) {\n\t\tif (deco->wlr_server_decoration->surface == surface) {\n\t\t\treturn deco;\n\t\t}\n\t}\n\treturn NULL;\n}\n"
  },
  {
    "path": "sway/desktop/idle_inhibit_v1.c",
    "content": "#include <stdlib.h>\n#include <wlr/types/wlr_idle_notify_v1.h>\n#include <wlr/types/wlr_session_lock_v1.h>\n#include \"log.h\"\n#include \"sway/desktop/idle_inhibit_v1.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/server.h\"\n\n\nstatic void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) {\n\twl_list_remove(&inhibitor->link);\n\twl_list_remove(&inhibitor->destroy.link);\n\tsway_idle_inhibit_v1_check_active();\n\tfree(inhibitor);\n}\n\nstatic void handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_idle_inhibitor_v1 *inhibitor =\n\t\twl_container_of(listener, inhibitor, destroy);\n\tsway_log(SWAY_DEBUG, \"Sway idle inhibitor destroyed\");\n\tdestroy_inhibitor(inhibitor);\n}\n\nvoid handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {\n\tstruct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;\n\tstruct sway_idle_inhibit_manager_v1 *manager =\n\t\twl_container_of(listener, manager, new_idle_inhibitor_v1);\n\tsway_log(SWAY_DEBUG, \"New sway idle inhibitor\");\n\n\tstruct sway_idle_inhibitor_v1 *inhibitor =\n\t\tcalloc(1, sizeof(struct sway_idle_inhibitor_v1));\n\tif (!inhibitor) {\n\t\treturn;\n\t}\n\n\tinhibitor->mode = INHIBIT_IDLE_APPLICATION;\n\tinhibitor->wlr_inhibitor = wlr_inhibitor;\n\twl_list_insert(&manager->inhibitors, &inhibitor->link);\n\n\tinhibitor->destroy.notify = handle_destroy;\n\twl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);\n\n\tsway_idle_inhibit_v1_check_active();\n}\n\nvoid handle_manager_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_idle_inhibit_manager_v1 *manager =\n\t\twl_container_of(listener, manager, manager_destroy);\n\n\twl_list_remove(&manager->manager_destroy.link);\n\twl_list_remove(&manager->new_idle_inhibitor_v1.link);\n}\n\nvoid sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view,\n\t\tenum sway_idle_inhibit_mode mode) {\n\tstruct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;\n\n\tstruct sway_idle_inhibitor_v1 *inhibitor =\n\t\tcalloc(1, sizeof(struct sway_idle_inhibitor_v1));\n\tif (!inhibitor) {\n\t\treturn;\n\t}\n\n\tinhibitor->mode = mode;\n\tinhibitor->view = view;\n\twl_list_insert(&manager->inhibitors, &inhibitor->link);\n\n\tinhibitor->destroy.notify = handle_destroy;\n\twl_signal_add(&view->events.unmap, &inhibitor->destroy);\n\n\tsway_idle_inhibit_v1_check_active();\n}\n\nstruct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(\n\t\tstruct sway_view *view) {\n\tstruct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;\n\tstruct sway_idle_inhibitor_v1 *inhibitor;\n\twl_list_for_each(inhibitor, &manager->inhibitors, link) {\n\t\tif (inhibitor->mode != INHIBIT_IDLE_APPLICATION &&\n\t\t\t\tinhibitor->view == view) {\n\t\t\treturn inhibitor;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view(\n\t\tstruct sway_view *view) {\n\tstruct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;\n\tstruct sway_idle_inhibitor_v1 *inhibitor;\n\twl_list_for_each(inhibitor, &manager->inhibitors, link) {\n\t\tif (inhibitor->mode == INHIBIT_IDLE_APPLICATION &&\n\t\t\t\tview_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {\n\t\t\treturn inhibitor;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nvoid sway_idle_inhibit_v1_user_inhibitor_destroy(\n\t\tstruct sway_idle_inhibitor_v1 *inhibitor) {\n\tif (!inhibitor) {\n\t\treturn;\n\t}\n\tif (!sway_assert(inhibitor->mode != INHIBIT_IDLE_APPLICATION,\n\t\t\t\t\"User should not be able to destroy application inhibitor\")) {\n\t\treturn;\n\t}\n\tdestroy_inhibitor(inhibitor);\n}\n\nbool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) {\n\tif (server.session_lock.lock) {\n\t\t// A session lock is active. In this case, only application inhibitors\n\t\t// on the session lock surface can have any effect.\n\t\tif (inhibitor->mode != INHIBIT_IDLE_APPLICATION) {\n\t\t\treturn false;\n\t\t}\n\t\tstruct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface;\n\t\tif (!wlr_session_lock_surface_v1_try_from_wlr_surface(wlr_surface)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn wlr_surface->mapped;\n\t}\n\n\tswitch (inhibitor->mode) {\n\tcase INHIBIT_IDLE_APPLICATION:;\n\t\tstruct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface;\n\t\tstruct wlr_layer_surface_v1 *layer_surface =\n\t\t\t\twlr_layer_surface_v1_try_from_wlr_surface(wlr_surface);\n\t\tif (layer_surface) {\n\t\t\t// Layer surfaces can be occluded but are always on screen after\n\t\t\t// they have been mapped.\n\t\t\treturn layer_surface->output && layer_surface->output->enabled &&\n\t\t\t\t\twlr_surface->mapped;\n\t\t}\n\n\t\t// If there is no view associated with the inhibitor, assume invisible\n\t\tstruct sway_view *view = view_from_wlr_surface(wlr_surface);\n\t\treturn view && view->container && view_is_visible(view);\n\tcase INHIBIT_IDLE_FOCUS:;\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tstruct sway_container *con = seat_get_focused_container(seat);\n\t\t\tif (con && con->view && con->view == inhibitor->view) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\tcase INHIBIT_IDLE_FULLSCREEN:\n\t\treturn inhibitor->view->container &&\n\t\t\tcontainer_is_fullscreen_or_child(inhibitor->view->container) &&\n\t\t\tview_is_visible(inhibitor->view);\n\tcase INHIBIT_IDLE_OPEN:\n\t\t// Inhibitor is destroyed on unmap so it must be open/mapped\n\t\treturn true;\n\tcase INHIBIT_IDLE_VISIBLE:\n\t\treturn view_is_visible(inhibitor->view);\n\t}\n\treturn false;\n}\n\nvoid sway_idle_inhibit_v1_check_active(void) {\n\tstruct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;\n\tstruct sway_idle_inhibitor_v1 *inhibitor;\n\tbool inhibited = false;\n\twl_list_for_each(inhibitor, &manager->inhibitors, link) {\n\t\tif ((inhibited = sway_idle_inhibit_v1_is_active(inhibitor))) {\n\t\t\tbreak;\n\t\t}\n\t}\n\twlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited);\n}\n\nbool sway_idle_inhibit_manager_v1_init(void) {\n\tstruct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;\n\n\tmanager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display);\n\tif (!manager->wlr_manager) {\n\t\treturn false;\n\t}\n\n\twl_signal_add(&manager->wlr_manager->events.new_inhibitor,\n\t\t&manager->new_idle_inhibitor_v1);\n\tmanager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1;\n\twl_signal_add(&manager->wlr_manager->events.destroy,\n\t\t&manager->manager_destroy);\n\tmanager->manager_destroy.notify = handle_manager_destroy;\n\twl_list_init(&manager->inhibitors);\n\n\treturn true;\n}\n"
  },
  {
    "path": "sway/desktop/launcher.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <wlr/types/wlr_xdg_activation_v1.h>\n#include \"sway/input/seat.h\"\n#include \"sway/output.h\"\n#include \"sway/desktop/launcher.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/node.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/tree/root.h\"\n#include \"log.h\"\n\n/**\n * Get the pid of a parent process given the pid of a child process.\n *\n * Returns the parent pid or NULL if the parent pid cannot be determined.\n */\nstatic pid_t get_parent_pid(pid_t child) {\n\tpid_t parent = -1;\n\tchar file_name[100];\n\tchar *buffer = NULL;\n\tconst char *sep = \" \";\n\tFILE *stat = NULL;\n\tsize_t buf_size = 0;\n\n\tsnprintf(file_name, sizeof(file_name), \"/proc/%d/stat\", child);\n\n\tif ((stat = fopen(file_name, \"r\"))) {\n\t\tif (getline(&buffer, &buf_size, stat) != -1) {\n\t\t\tstrtok(buffer, sep); // pid\n\t\t\tstrtok(NULL, sep);   // executable name\n\t\t\tstrtok(NULL, sep);   // state\n\t\t\tchar *token = strtok(NULL, sep);   // parent pid\n\t\t\tparent = strtol(token, NULL, 10);\n\t\t}\n\t\tfree(buffer);\n\t\tfclose(stat);\n\t}\n\n\tif (parent) {\n\t\treturn (parent == child) ? -1 : parent;\n\t}\n\n\treturn -1;\n}\n\nvoid launcher_ctx_consume(struct launcher_ctx *ctx) {\n\t// The view is now responsible for destroying this ctx\n\twl_list_remove(&ctx->token_destroy.link);\n\twl_list_init(&ctx->token_destroy.link);\n\n\tif (!ctx->activated) {\n\t\t// An unactivated token hasn't been destroyed yet\n\t\twlr_xdg_activation_token_v1_destroy(ctx->token);\n\t}\n\tctx->token = NULL;\n\n\t// Prevent additional matches\n\twl_list_remove(&ctx->link);\n\twl_list_init(&ctx->link);\n}\n\nvoid launcher_ctx_destroy(struct launcher_ctx *ctx) {\n\tif (ctx == NULL) {\n\t\treturn;\n\t}\n\twl_list_remove(&ctx->node_destroy.link);\n\twl_list_remove(&ctx->token_destroy.link);\n\tif (ctx->seat) {\n\t\twl_list_remove(&ctx->seat_destroy.link);\n\t}\n\twl_list_remove(&ctx->link);\n\twlr_xdg_activation_token_v1_destroy(ctx->token);\n\tfree(ctx->fallback_name);\n\tfree(ctx);\n}\n\nstruct launcher_ctx *launcher_ctx_find_pid(pid_t pid) {\n\tif (wl_list_empty(&server.pending_launcher_ctxs)) {\n\t\treturn NULL;\n\t}\n\n\tstruct launcher_ctx *ctx = NULL;\n\tsway_log(SWAY_DEBUG, \"Looking up workspace for pid %d\", pid);\n\n\tdo {\n\t\tstruct launcher_ctx *_ctx = NULL;\n\t\twl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) {\n\t\t\tif (pid == _ctx->pid) {\n\t\t\t\tctx = _ctx;\n\t\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\"found %s match for pid %d: %s\",\n\t\t\t\t\tnode_type_to_str(ctx->node->type), pid, node_get_name(ctx->node));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tpid = get_parent_pid(pid);\n\t} while (pid > 1);\n\n\treturn ctx;\n}\n\nstruct sway_workspace *launcher_ctx_get_workspace(\n\t\tstruct launcher_ctx *ctx) {\n\tstruct sway_workspace *ws = NULL;\n\tstruct sway_output *output = NULL;\n\n\tswitch (ctx->node->type) {\n\tcase N_CONTAINER:\n\t\t// Unimplemented\n\t\t// TODO: add container matching?\n\t\tws = ctx->node->sway_container->pending.workspace;\n\t\tbreak;\n\tcase N_WORKSPACE:\n\t\tws = ctx->node->sway_workspace;\n\t\tbreak;\n\tcase N_OUTPUT:\n\t\toutput = ctx->node->sway_output;\n\t\tws = workspace_by_name(ctx->fallback_name);\n\t\tif (!ws) {\n\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\"Creating workspace %s for pid %d because it disappeared\",\n\t\t\t\t\tctx->fallback_name, ctx->pid);\n\t\t\tif (!output->enabled) {\n\t\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\t\"Workspace output %s is disabled, trying another one\",\n\t\t\t\t\t\toutput->wlr_output->name);\n\t\t\t\toutput = NULL;\n\t\t\t}\n\t\t\tws = workspace_create(output, ctx->fallback_name);\n\t\t}\n\t\tbreak;\n\tcase N_ROOT:\n\t\tws = workspace_create(NULL, ctx->fallback_name);\n\t\tbreak;\n\t}\n\n\treturn ws;\n}\n\nstatic void ctx_handle_node_destroy(struct wl_listener *listener, void *data) {\n\tstruct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy);\n\tswitch (ctx->node->type) {\n\tcase N_CONTAINER:\n\t\t// Unimplemented\n\t\tbreak;\n\tcase N_WORKSPACE:;\n\t\tstruct sway_workspace *ws = ctx->node->sway_workspace;\n\t\twl_list_remove(&ctx->node_destroy.link);\n\t\twl_list_init(&ctx->node_destroy.link);\n\t\t// We want to save this ws name to recreate later, hopefully on the\n\t\t// same output\n\t\tfree(ctx->fallback_name);\n\t\tctx->fallback_name = strdup(ws->name);\n\t\tif (!ws->output || ws->output->node.destroying) {\n\t\t\t// If the output is being destroyed it would be pointless to track\n\t\t\t// If the output is being disabled, we'll find out if it's still\n\t\t\t// disabled when we try to match it.\n\t\t\tctx->node = &root->node;\n\t\t\tbreak;\n\t\t}\n\t\tctx->node = &ws->output->node;\n\t\twl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);\n\t\tbreak;\n\tcase N_OUTPUT:\n\t\twl_list_remove(&ctx->node_destroy.link);\n\t\twl_list_init(&ctx->node_destroy.link);\n\t\t// We'll make the ws ctx->name somewhere else\n\t\tctx->node = &root->node;\n\t\tbreak;\n\tcase N_ROOT:\n\t\t// Unreachable\n\t\tbreak;\n\t}\n}\n\nstatic void token_handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy);\n\tctx->token = NULL;\n\tlauncher_ctx_destroy(ctx);\n}\n\nstruct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token,\n\t\tstruct sway_node *node) {\n\tstruct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx));\n\n\tconst char *fallback_name = NULL;\n\tstruct sway_workspace *ws = NULL;\n\tswitch (node->type) {\n\tcase N_CONTAINER:\n\t\t// Unimplemented\n\t\tfree(ctx);\n\t\treturn NULL;\n\tcase N_WORKSPACE:\n\t\tws = node->sway_workspace;\n\t\tfallback_name = ws->name;\n\t\tbreak;\n\tcase N_OUTPUT:;\n\t\tstruct sway_output *output = node->sway_output;\n\t\tws = output_get_active_workspace(output);\n\t\tfallback_name = ws ? ws->name : NULL;\n\t\tbreak;\n\tcase N_ROOT:\n\t\t// Unimplemented\n\t\tfree(ctx);\n\t\treturn NULL;\n\t}\n\n\tif (!fallback_name) {\n\t\t// TODO: implement a better fallback.\n\t\tfree(ctx);\n\t\treturn NULL;\n\t}\n\n\tctx->fallback_name = strdup(fallback_name);\n\tctx->token = token;\n\tctx->node = node;\n\t// Having surface set means that the focus check in wlroots has passed\n\tctx->had_focused_surface = token->surface != NULL;\n\n\tctx->node_destroy.notify = ctx_handle_node_destroy;\n\twl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);\n\n\tctx->token_destroy.notify = token_handle_destroy;\n\twl_signal_add(&token->events.destroy, &ctx->token_destroy);\n\n\twl_list_init(&ctx->link);\n\twl_list_insert(&server.pending_launcher_ctxs, &ctx->link);\n\n\ttoken->data = ctx;\n\treturn ctx;\n}\n\nstatic void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) {\n\tstruct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy);\n\tctx->seat = NULL;\n\twl_list_remove(&ctx->seat_destroy.link);\n}\n\n// Creates a context with a new token for the internal launcher\nstruct launcher_ctx *launcher_ctx_create_internal(void) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_workspace *ws = seat_get_focused_workspace(seat);\n\tif (!ws) {\n\t\tsway_log(SWAY_DEBUG, \"Failed to create launch context. No workspace.\");\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_xdg_activation_token_v1 *token =\n\t\twlr_xdg_activation_token_v1_create(server.xdg_activation_v1);\n\n\tstruct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node);\n\tif (!ctx) {\n\t\twlr_xdg_activation_token_v1_destroy(token);\n\t\treturn NULL;\n\t}\n\tctx->seat = seat;\n\tctx->seat_destroy.notify = launch_ctx_handle_seat_destroy;\n\twl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy);\n\n\treturn ctx;\n}\n\nconst char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {\n\tconst char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);\n\treturn token;\n}\n"
  },
  {
    "path": "sway/desktop/layer_shell.c",
    "content": "#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <wayland-server-core.h>\n#include <wlr/types/wlr_fractional_scale_v1.h>\n#include <wlr/types/wlr_layer_shell_v1.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_scene.h>\n#include <wlr/types/wlr_subcompositor.h>\n#include <wlr/types/wlr_xdg_shell.h>\n#include \"log.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/layers.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/workspace.h\"\n\nstruct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(\n\t\tstruct wlr_surface *surface) {\n\tstruct wlr_layer_surface_v1 *layer;\n\tdo {\n\t\tif (!surface) {\n\t\t\treturn NULL;\n\t\t}\n\t\t// Topmost layer surface\n\t\tif ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) {\n\t\t\treturn layer;\n\t\t}\n\t\t// Layer subsurface\n\t\tif (wlr_subsurface_try_from_wlr_surface(surface)) {\n\t\t\tsurface = wlr_surface_get_root_surface(surface);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Layer surface popup\n\t\tstruct wlr_xdg_surface *xdg_surface = NULL;\n\t\tif ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) &&\n\t\t\t\txdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) {\n\t\t\tif (!xdg_surface->popup->parent) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tsurface = wlr_surface_get_root_surface(xdg_surface->popup->parent);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Return early if the surface is not a layer/xdg_popup/sub surface\n\t\treturn NULL;\n\t} while (true);\n}\n\nstatic void arrange_surface(struct sway_output *output, const struct wlr_box *full_area,\n\t\tstruct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) {\n\tstruct wlr_scene_node *node;\n\twl_list_for_each(node, &tree->children, link) {\n\t\tstruct sway_layer_surface *surface = scene_descriptor_try_get(node,\n\t\t\tSWAY_SCENE_DESC_LAYER_SHELL);\n\t\t// surface could be null during destruction\n\t\tif (!surface) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!surface->scene->layer_surface->initialized) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((surface->scene->layer_surface->current.exclusive_zone > 0) != exclusive) {\n\t\t\tcontinue;\n\t\t}\n\n\t\twlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);\n\t}\n}\n\nvoid arrange_layers(struct sway_output *output) {\n\tstruct wlr_box usable_area = { 0 };\n\twlr_output_effective_resolution(output->wlr_output,\n\t\t\t&usable_area.width, &usable_area.height);\n\tconst struct wlr_box full_area = usable_area;\n\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true);\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true);\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true);\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true);\n\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false);\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false);\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false);\n\tarrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false);\n\n\tif (!wlr_box_equal(&usable_area, &output->usable_area)) {\n\t\tsway_log(SWAY_DEBUG, \"Usable area changed, rearranging output\");\n\t\toutput->usable_area = usable_area;\n\t\tarrange_output(output);\n\t} else {\n\t\tarrange_popups(root->layers.popup);\n\t}\n\n\t// Find topmost keyboard interactive layer, if such a layer exists\n\tstruct wlr_scene_tree *layers_above_shell[] = {\n\t\toutput->layers.shell_overlay,\n\t\toutput->layers.shell_top,\n\t};\n\tsize_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);\n\tstruct wlr_scene_node *node;\n\tstruct sway_layer_surface *topmost = NULL;\n\tfor (size_t i = 0; i < nlayers; ++i) {\n\t\twl_list_for_each_reverse(node,\n\t\t\t\t&layers_above_shell[i]->children, link) {\n\t\t\tstruct sway_layer_surface *surface = scene_descriptor_try_get(node,\n\t\t\t\tSWAY_SCENE_DESC_LAYER_SHELL);\n\t\t\tif (surface && surface->layer_surface->current.keyboard_interactive\n\t\t\t\t\t== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE &&\n\t\t\t\t\tsurface->layer_surface->surface->mapped) {\n\t\t\t\ttopmost = surface;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (topmost != NULL) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat->has_exclusive_layer = false;\n\t\tif (topmost != NULL) {\n\t\t\tseat_set_focus_layer(seat, topmost->layer_surface);\n\t\t} else if (seat->focused_layer &&\n\t\t\t\tseat->focused_layer->current.keyboard_interactive\n\t\t\t\t\t!= ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {\n\t\t\tseat_set_focus_layer(seat, NULL);\n\t\t}\n\t}\n}\n\nstatic struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output,\n\t\tenum zwlr_layer_shell_v1_layer type) {\n\tswitch (type) {\n\tcase ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:\n\t\treturn output->layers.shell_background;\n\tcase ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:\n\t\treturn output->layers.shell_bottom;\n\tcase ZWLR_LAYER_SHELL_V1_LAYER_TOP:\n\t\treturn output->layers.shell_top;\n\tcase ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:\n\t\treturn output->layers.shell_overlay;\n\t}\n\n\tsway_assert(false, \"unreachable\");\n\treturn NULL;\n}\n\nstatic struct sway_layer_surface *sway_layer_surface_create(\n\t\tstruct wlr_scene_layer_surface_v1 *scene) {\n\tstruct sway_layer_surface *surface = calloc(1, sizeof(*surface));\n\tif (!surface) {\n\t\tsway_log(SWAY_ERROR, \"Could not allocate a scene_layer surface\");\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup);\n\tif (!popups) {\n\t\tsway_log(SWAY_ERROR, \"Could not allocate a scene_layer popup node\");\n\t\tfree(surface);\n\t\treturn NULL;\n\t}\n\n\tsurface->desc.relative = &scene->tree->node;\n\n\tif (!scene_descriptor_assign(&popups->node,\n\t\t\tSWAY_SCENE_DESC_POPUP, &surface->desc)) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate a popup scene descriptor\");\n\t\twlr_scene_node_destroy(&popups->node);\n\t\tfree(surface);\n\t\treturn NULL;\n\t}\n\n\tsurface->tree = scene->tree;\n\tsurface->scene = scene;\n\tsurface->layer_surface = scene->layer_surface;\n\tsurface->popups = popups;\n\tsurface->layer_surface->data = surface;\n\n\treturn surface;\n}\n\nstatic struct sway_layer_surface *find_mapped_layer_by_client(\n\t\tstruct wl_client *client, struct sway_output *ignore_output) {\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tif (output == ignore_output) {\n\t\t\tcontinue;\n\t\t}\n\t\t// For now we'll only check the overlay layer\n\t\tstruct wlr_scene_node *node;\n\t\twl_list_for_each (node, &output->layers.shell_overlay->children, link) {\n\t\t\tstruct sway_layer_surface *surface = scene_descriptor_try_get(node,\n\t\t\t\tSWAY_SCENE_DESC_LAYER_SHELL);\n\t\t\tif (!surface) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tstruct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;\n\t\t\tstruct wl_resource *resource = layer_surface->resource;\n\t\t\tif (wl_resource_get_client(resource) == client\n\t\t\t\t\t&& layer_surface->surface->mapped) {\n\t\t\t\treturn surface;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void handle_node_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_surface *layer =\n\t\twl_container_of(listener, layer, node_destroy);\n\n\t// destroy the scene descriptor straight away if it exists, otherwise\n\t// we will try to reflow still considering the destroyed node.\n\tscene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL);\n\n\t// Determine if this layer is being used by an exclusive client. If it is,\n\t// try and find another layer owned by this client to pass focus to.\n\tstruct sway_seat *seat = input_manager_get_default_seat();\n\tstruct wl_client *client =\n\t\twl_resource_get_client(layer->layer_surface->resource);\n\tif (!server.session_lock.lock) {\n\t\tstruct sway_layer_surface *consider_layer =\n\t\t\tfind_mapped_layer_by_client(client, layer->output);\n\t\tif (consider_layer) {\n\t\t\tseat_set_focus_layer(seat, consider_layer->layer_surface);\n\t\t}\n\t}\n\n\tif (layer->output) {\n\t\tarrange_layers(layer->output);\n\t\ttransaction_commit_dirty();\n\t}\n\n\twlr_scene_node_destroy(&layer->popups->node);\n\n\twl_list_remove(&layer->map.link);\n\twl_list_remove(&layer->unmap.link);\n\twl_list_remove(&layer->surface_commit.link);\n\twl_list_remove(&layer->node_destroy.link);\n\twl_list_remove(&layer->new_popup.link);\n\n\tlayer->layer_surface->data = NULL;\n\n\twl_list_remove(&layer->link);\n\tfree(layer);\n}\n\nstatic void handle_surface_commit(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_surface *surface =\n\t\twl_container_of(listener, surface, surface_commit);\n\n\tstruct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;\n\tuint32_t committed = layer_surface->current.committed;\n\tif (layer_surface->initialized && committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {\n\t\tenum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;\n\t\tstruct wlr_scene_tree *output_layer = sway_layer_get_scene(\n\t\t\tsurface->output, layer_type);\n\t\twlr_scene_node_reparent(&surface->scene->tree->node, output_layer);\n\t}\n\n\tif (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {\n\t\tsurface->mapped = layer_surface->surface->mapped;\n\t\tarrange_layers(surface->output);\n\t\ttransaction_commit_dirty();\n\t}\n}\n\nstatic void handle_map(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_surface *surface = wl_container_of(listener,\n\t\t\tsurface, map);\n\n\tstruct wlr_layer_surface_v1 *layer_surface =\n\t\t\t\tsurface->scene->layer_surface;\n\n\t// focus on new surface\n\tif (layer_surface->current.keyboard_interactive &&\n\t\t\t(layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||\n\t\t\tlayer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {\n\t\tstruct sway_seat *seat;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\t// but only if the currently focused layer has a lower precedence\n\t\t\tif (!seat->focused_layer ||\n\t\t\t\t\tseat->focused_layer->current.layer >= layer_surface->current.layer) {\n\t\t\t\tseat_set_focus_layer(seat, layer_surface);\n\t\t\t}\n\t\t}\n\t\tarrange_layers(surface->output);\n\t}\n\n\tcursor_rebase_all();\n}\n\nstatic void handle_unmap(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_surface *surface = wl_container_of(\n\t\t\tlistener, surface, unmap);\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tif (seat->focused_layer == surface->layer_surface) {\n\t\t\tseat_set_focus_layer(seat, NULL);\n\t\t}\n\t}\n\n\tcursor_rebase_all();\n}\n\nstatic void popup_handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_popup *popup =\n\t\twl_container_of(listener, popup, destroy);\n\n\twl_list_remove(&popup->destroy.link);\n\twl_list_remove(&popup->new_popup.link);\n\twl_list_remove(&popup->commit.link);\n\twl_list_remove(&popup->reposition.link);\n\tfree(popup);\n}\n\nstatic void popup_unconstrain(struct sway_layer_popup *popup) {\n\tstruct wlr_xdg_popup *wlr_popup = popup->wlr_popup;\n\tstruct sway_output *output = popup->toplevel->output;\n\n\t// if a client tries to create a popup while we are in the process of destroying\n\t// its output, don't crash.\n\tif (!output) {\n\t\treturn;\n\t}\n\n\tint lx, ly;\n\twlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly);\n\n\t// the output box expressed in the coordinate system of the toplevel parent\n\t// of the popup\n\tstruct wlr_box output_toplevel_sx_box = {\n\t\t.x = output->lx - lx,\n\t\t.y = output->ly - ly,\n\t\t.width = output->width,\n\t\t.height = output->height,\n\t};\n\n\twlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);\n}\n\nstatic void popup_handle_commit(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_popup *popup = wl_container_of(listener, popup, commit);\n\tif (popup->wlr_popup->base->initial_commit) {\n\t\tpopup_unconstrain(popup);\n\t}\n}\n\nstatic void popup_handle_reposition(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_popup *popup = wl_container_of(listener, popup, reposition);\n\tpopup_unconstrain(popup);\n}\n\nstatic void popup_handle_new_popup(struct wl_listener *listener, void *data);\n\nstatic struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup,\n\t\tstruct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) {\n\tstruct sway_layer_popup *popup = calloc(1, sizeof(*popup));\n\tif (popup == NULL) {\n\t\treturn NULL;\n\t}\n\n\tpopup->toplevel = toplevel;\n\tpopup->wlr_popup = wlr_popup;\n\tpopup->scene = wlr_scene_xdg_surface_create(parent,\n\t\twlr_popup->base);\n\n\tif (!popup->scene) {\n\t\tfree(popup);\n\t\treturn NULL;\n\t}\n\n\tpopup->destroy.notify = popup_handle_destroy;\n\twl_signal_add(&wlr_popup->events.destroy, &popup->destroy);\n\tpopup->new_popup.notify = popup_handle_new_popup;\n\twl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);\n\tpopup->commit.notify = popup_handle_commit;\n\twl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);\n\tpopup->reposition.notify = popup_handle_reposition;\n\twl_signal_add(&wlr_popup->events.reposition, &popup->reposition);\n\n\treturn popup;\n}\n\nstatic void popup_handle_new_popup(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_popup *sway_layer_popup =\n\t\twl_container_of(listener, sway_layer_popup, new_popup);\n\tstruct wlr_xdg_popup *wlr_popup = data;\n\tcreate_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene);\n}\n\nstatic void handle_new_popup(struct wl_listener *listener, void *data) {\n\tstruct sway_layer_surface *sway_layer_surface =\n\t\twl_container_of(listener, sway_layer_surface, new_popup);\n\tstruct wlr_xdg_popup *wlr_popup = data;\n\tcreate_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups);\n}\n\nvoid handle_layer_shell_surface(struct wl_listener *listener, void *data) {\n\tstruct wlr_layer_surface_v1 *layer_surface = data;\n\tsway_log(SWAY_DEBUG, \"new layer surface: namespace %s layer %d anchor %\" PRIu32\n\t\t\t\" size %\" PRIu32 \"x%\" PRIu32 \" margin %\" PRIu32 \",%\" PRIu32 \",%\" PRIu32 \",%\" PRIu32 \",\",\n\t\tlayer_surface->namespace,\n\t\tlayer_surface->pending.layer,\n\t\tlayer_surface->pending.anchor,\n\t\tlayer_surface->pending.desired_width,\n\t\tlayer_surface->pending.desired_height,\n\t\tlayer_surface->pending.margin.top,\n\t\tlayer_surface->pending.margin.right,\n\t\tlayer_surface->pending.margin.bottom,\n\t\tlayer_surface->pending.margin.left);\n\n\tif (!layer_surface->output) {\n\t\t// Assign last active output\n\t\tstruct sway_output *output = NULL;\n\t\tstruct sway_seat *seat = input_manager_get_default_seat();\n\t\tif (seat) {\n\t\t\tstruct sway_workspace *ws = seat_get_focused_workspace(seat);\n\t\t\tif (ws != NULL) {\n\t\t\t\toutput = ws->output;\n\t\t\t}\n\t\t}\n\t\tif (!output || output == root->fallback_output) {\n\t\t\tif (!root->outputs->length) {\n\t\t\t\tsway_log(SWAY_ERROR,\n\t\t\t\t\t\t\"no output to auto-assign layer surface '%s' to\",\n\t\t\t\t\t\tlayer_surface->namespace);\n\t\t\t\twlr_layer_surface_v1_destroy(layer_surface);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toutput = root->outputs->items[0];\n\t\t}\n\t\tlayer_surface->output = output->wlr_output;\n\t}\n\n\tstruct sway_output *output = layer_surface->output->data;\n\n\tenum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer;\n\tstruct wlr_scene_tree *output_layer = sway_layer_get_scene(\n\t\toutput, layer_type);\n\tstruct wlr_scene_layer_surface_v1 *scene_surface =\n\t\twlr_scene_layer_surface_v1_create(output_layer, layer_surface);\n\tif (!scene_surface) {\n\t\tsway_log(SWAY_ERROR, \"Could not allocate a layer_surface_v1\");\n\t\treturn;\n\t}\n\n\tstruct sway_layer_surface *surface =\n\t\tsway_layer_surface_create(scene_surface);\n\tif (!surface) {\n\t\twlr_layer_surface_v1_destroy(layer_surface);\n\n\t\tsway_log(SWAY_ERROR, \"Could not allocate a sway_layer_surface\");\n\t\treturn;\n\t}\n\n\tif (!scene_descriptor_assign(&scene_surface->tree->node,\n\t\t\tSWAY_SCENE_DESC_LAYER_SHELL, surface)) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate a layer surface descriptor\");\n\t\t// destroying the layer_surface will also destroy its corresponding\n\t\t// scene node\n\t\twlr_layer_surface_v1_destroy(layer_surface);\n\t\treturn;\n\t}\n\n\tsurface->output = output;\n\twl_list_insert(&output->layer_surfaces, &surface->link);\n\n\t// now that the surface's output is known, we can advertise its scale\n\twlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface,\n\t\tlayer_surface->output->scale);\n\twlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface,\n\t\tceil(layer_surface->output->scale));\n\n\tsurface->surface_commit.notify = handle_surface_commit;\n\twl_signal_add(&layer_surface->surface->events.commit,\n\t\t&surface->surface_commit);\n\tsurface->map.notify = handle_map;\n\twl_signal_add(&layer_surface->surface->events.map, &surface->map);\n\tsurface->unmap.notify = handle_unmap;\n\twl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);\n\tsurface->new_popup.notify = handle_new_popup;\n\twl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);\n\n\tsurface->node_destroy.notify = handle_node_destroy;\n\twl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy);\n}\n\nvoid destroy_layers(struct sway_output *output) {\n\tstruct sway_layer_surface *layer, *layer_tmp;\n\twl_list_for_each_safe(layer, layer_tmp, &output->layer_surfaces, link) {\n\t\tlayer->output = NULL;\n\t\twlr_layer_surface_v1_destroy(layer->layer_surface);\n\t}\n}\n"
  },
  {
    "path": "sway/desktop/output.c",
    "content": "#include <assert.h>\n#include <stdlib.h>\n#include <strings.h>\n#include <time.h>\n#include <wayland-server-core.h>\n#include <wlr/config.h>\n#include <wlr/backend/headless.h>\n#include <wlr/render/swapchain.h>\n#include <wlr/render/wlr_renderer.h>\n#include <wlr/types/wlr_buffer.h>\n#include <wlr/types/wlr_alpha_modifier_v1.h>\n#include <wlr/types/wlr_gamma_control_v1.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_output_management_v1.h>\n#include <wlr/types/wlr_output_power_management_v1.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_presentation_time.h>\n#include <wlr/types/wlr_compositor.h>\n#include <wlr/util/region.h>\n#include <wlr/util/transform.h>\n#include \"config.h\"\n#include \"log.h\"\n#include \"sway/config.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/layers.h\"\n#include \"sway/output.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n\n#if WLR_HAS_DRM_BACKEND\n#include <wlr/backend/drm.h>\n#include <wlr/types/wlr_drm_lease_v1.h>\n#endif\n\nbool output_match_name_or_id(struct sway_output *output,\n\t\tconst char *name_or_id) {\n\tif (strcmp(name_or_id, \"*\") == 0) {\n\t\treturn true;\n\t}\n\n\tchar identifier[128];\n\toutput_get_identifier(identifier, sizeof(identifier), output);\n\treturn strcasecmp(identifier, name_or_id) == 0\n\t\t|| strcasecmp(output->wlr_output->name, name_or_id) == 0;\n}\n\nstruct sway_output *output_by_name_or_id(const char *name_or_id) {\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tif (output_match_name_or_id(output, name_or_id)) {\n\t\t\treturn output;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_output *all_output_by_name_or_id(const char *name_or_id) {\n\tstruct sway_output *output;\n\twl_list_for_each(output, &root->all_outputs, link) {\n\t\tif (output_match_name_or_id(output, name_or_id)) {\n\t\t\treturn output;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\nstruct sway_workspace *output_get_active_workspace(struct sway_output *output) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);\n\tif (!focus) {\n\t\tif (!output->workspaces->length) {\n\t\t\treturn NULL;\n\t\t}\n\t\treturn output->workspaces->items[0];\n\t}\n\treturn focus->sway_workspace;\n}\n\nstruct send_frame_done_data {\n\tstruct timespec when;\n\tint msec_until_refresh;\n\tstruct sway_output *output;\n};\n\nstruct buffer_timer {\n\tstruct wl_listener destroy;\n\tstruct wl_event_source *frame_done_timer;\n};\n\nstatic int handle_buffer_timer(void *data) {\n\tstruct wlr_scene_surface *scene_surface = data;\n\n\tstruct timespec now;\n\tclock_gettime(CLOCK_MONOTONIC, &now);\n\twlr_scene_surface_send_frame_done(scene_surface, &now);\n\treturn 0;\n}\n\nstatic void handle_buffer_timer_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct buffer_timer *timer = wl_container_of(listener, timer, destroy);\n\n\twl_list_remove(&timer->destroy.link);\n\twl_event_source_remove(timer->frame_done_timer);\n\tfree(timer);\n}\n\nstatic struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_surface *scene_surface) {\n\tstruct wlr_scene_buffer *buffer = scene_surface->buffer;\n\n\tstruct buffer_timer *timer =\n\t\tscene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER);\n\tif (timer) {\n\t\treturn timer;\n\t}\n\n\ttimer = calloc(1, sizeof(struct buffer_timer));\n\tif (!timer) {\n\t\treturn NULL;\n\t}\n\n\ttimer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,\n\t\thandle_buffer_timer, scene_surface);\n\tif (!timer->frame_done_timer) {\n\t\tfree(timer);\n\t\treturn NULL;\n\t}\n\n\tscene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer);\n\n\ttimer->destroy.notify = handle_buffer_timer_destroy;\n\twl_signal_add(&buffer->node.events.destroy, &timer->destroy);\n\n\treturn timer;\n}\n\nstatic void send_frame_done_iterator(struct wlr_scene_buffer *buffer,\n\t\tint x, int y, void *user_data) {\n\tstruct send_frame_done_data *data = user_data;\n\tstruct sway_output *output = data->output;\n\tint view_max_render_time = 0;\n\n\tif (buffer->primary_output != data->output->scene_output) {\n\t\treturn;\n\t}\n\n\tstruct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer);\n\tif (scene_surface == NULL) {\n\t\treturn;\n\t}\n\n\tstruct wlr_scene_node *current = &buffer->node;\n\twhile (true) {\n\t\tstruct sway_view *view = scene_descriptor_try_get(current,\n\t\t\tSWAY_SCENE_DESC_VIEW);\n\t\tif (view) {\n\t\t\tview_max_render_time = view->max_render_time;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!current->parent) {\n\t\t\tbreak;\n\t\t}\n\n\t\tcurrent = &current->parent->node;\n\t}\n\n\tint delay = data->msec_until_refresh - output->max_render_time\n\t\t\t- view_max_render_time;\n\n\tstruct buffer_timer *timer = NULL;\n\n\tif (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {\n\t\ttimer = buffer_timer_get_or_create(scene_surface);\n\t}\n\n\tif (timer) {\n\t\twl_event_source_timer_update(timer->frame_done_timer, delay);\n\t} else {\n\t\twlr_scene_surface_send_frame_done(scene_surface, &data->when);\n\t}\n}\n\nstatic enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,\n\t\tstruct wlr_scene_buffer *buffer) {\n\t// if we are scaling down, we should always choose linear\n\tif (buffer->dst_width > 0 && buffer->dst_height > 0 && (\n\t\t\tbuffer->dst_width < buffer->WLR_PRIVATE.buffer_width ||\n\t\t\tbuffer->dst_height < buffer->WLR_PRIVATE.buffer_height)) {\n\t\treturn WLR_SCALE_FILTER_BILINEAR;\n\t}\n\n\tswitch (output->scale_filter) {\n\tcase SCALE_FILTER_LINEAR:\n\t\treturn WLR_SCALE_FILTER_BILINEAR;\n\tcase SCALE_FILTER_NEAREST:\n\t\treturn WLR_SCALE_FILTER_NEAREST;\n\tdefault:\n\t\tabort(); // unreachable\n\t}\n}\n\nvoid output_configure_scene(struct sway_output *output,\n\t\tstruct wlr_scene_node *node, float opacity) {\n\tif (!node->enabled) {\n\t\treturn;\n\t}\n\n\tstruct sway_container *con =\n\t\tscene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER);\n\tif (con) {\n\t\topacity = con->alpha;\n\t}\n\n\tif (node->type == WLR_SCENE_NODE_BUFFER) {\n\t\tstruct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);\n\t\tstruct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(buffer);\n\n\t\tif (surface) {\n\t\t\tconst struct wlr_alpha_modifier_surface_v1_state *alpha_modifier_state =\n\t\t\t\twlr_alpha_modifier_v1_get_surface_state(surface->surface);\n\t\t\tif (alpha_modifier_state != NULL) {\n\t\t\t\topacity *= (float)alpha_modifier_state->multiplier;\n\t\t\t}\n\t\t}\n\n\t\t// hack: don't call the scene setter because that will damage all outputs\n\t\t// We don't want to damage outputs that aren't our current output that\n\t\t// we're configuring\n\t\tif (output) {\n\t\t\tbuffer->filter_mode = get_scale_filter(output, buffer);\n\t\t}\n\n\t\twlr_scene_buffer_set_opacity(buffer, opacity);\n\t} else if (node->type == WLR_SCENE_NODE_TREE) {\n\t\tstruct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);\n\t\tstruct wlr_scene_node *node;\n\t\twl_list_for_each(node, &tree->children, link) {\n\t\t\toutput_configure_scene(output, node, opacity);\n\t\t}\n\t}\n}\n\nstatic bool output_can_tear(struct sway_output *output) {\n\tstruct sway_workspace *workspace = output->current.active_workspace;\n\tif (!workspace) {\n\t\treturn false;\n\t}\n\n\tstruct sway_container *fullscreen_con = root->fullscreen_global;\n\tif (!fullscreen_con) {\n\t\tfullscreen_con = workspace->current.fullscreen;\n\t}\n\tif (fullscreen_con && fullscreen_con->view) {\n\t\treturn (output->allow_tearing && view_can_tear(fullscreen_con->view));\n\t}\n\n\treturn false;\n}\n\nstatic int output_repaint_timer_handler(void *data) {\n\tstruct sway_output *output = data;\n\n\toutput->wlr_output->frame_pending = false;\n\tif (!output->wlr_output->enabled) {\n\t\treturn 0;\n\t}\n\n\toutput_configure_scene(output, &root->root_scene->tree.node, 1.0f);\n\n\tstruct wlr_scene_output_state_options opts = {\n\t\t.color_transform = output->color_transform,\n\t};\n\n\tstruct wlr_scene_output *scene_output = output->scene_output;\n\tif (!wlr_scene_output_needs_frame(scene_output)) {\n\t\treturn 0;\n\t}\n\n\tstruct wlr_output_state pending;\n\twlr_output_state_init(&pending);\n\tif (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) {\n\t\twlr_output_state_finish(&pending);\n\t\treturn 0;\n\t}\n\n\tif (output_can_tear(output)) {\n\t\tpending.tearing_page_flip = true;\n\n\t\tif (!wlr_output_test_state(output->wlr_output, &pending)) {\n\t\t\tsway_log(SWAY_DEBUG, \"Output test failed on '%s', retrying without tearing page-flip\",\n\t\t\t\toutput->wlr_output->name);\n\t\t\tpending.tearing_page_flip = false;\n\t\t}\n\t}\n\n\tif (!wlr_output_commit_state(output->wlr_output, &pending)) {\n\t\tsway_log(SWAY_ERROR, \"Page-flip failed on output %s\", output->wlr_output->name);\n\t}\n\twlr_output_state_finish(&pending);\n\treturn 0;\n}\n\nstatic void handle_frame(struct wl_listener *listener, void *user_data) {\n\tstruct sway_output *output =\n\t\twl_container_of(listener, output, frame);\n\tif (!output->enabled || !output->wlr_output->enabled) {\n\t\treturn;\n\t}\n\n\t// Compute predicted milliseconds until the next refresh. It's used for\n\t// delaying both output rendering and surface frame callbacks.\n\tint msec_until_refresh = 0;\n\n\tif (output->max_render_time != 0) {\n\t\tstruct timespec now;\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\n\t\tconst long NSEC_IN_SECONDS = 1000000000;\n\t\tstruct timespec predicted_refresh = output->last_presentation;\n\t\tpredicted_refresh.tv_nsec += output->refresh_nsec % NSEC_IN_SECONDS;\n\t\tpredicted_refresh.tv_sec += output->refresh_nsec / NSEC_IN_SECONDS;\n\t\tif (predicted_refresh.tv_nsec >= NSEC_IN_SECONDS) {\n\t\t\tpredicted_refresh.tv_sec += 1;\n\t\t\tpredicted_refresh.tv_nsec -= NSEC_IN_SECONDS;\n\t\t}\n\n\t\t// If the predicted refresh time is before the current time then\n\t\t// there's no point in delaying.\n\t\t//\n\t\t// We only check tv_sec because if the predicted refresh time is less\n\t\t// than a second before the current time, then msec_until_refresh will\n\t\t// end up slightly below zero, which will effectively disable the delay\n\t\t// without potential disastrous negative overflows that could occur if\n\t\t// tv_sec was not checked.\n\t\tif (predicted_refresh.tv_sec >= now.tv_sec) {\n\t\t\tlong nsec_until_refresh\n\t\t\t\t= (predicted_refresh.tv_sec - now.tv_sec) * NSEC_IN_SECONDS\n\t\t\t\t\t+ (predicted_refresh.tv_nsec - now.tv_nsec);\n\n\t\t\t// We want msec_until_refresh to be conservative, that is, floored.\n\t\t\t// If we have 7.9 msec until refresh, we better compute the delay\n\t\t\t// as if we had only 7 msec, so that we don't accidentally delay\n\t\t\t// more than necessary and miss a frame.\n\t\t\tmsec_until_refresh = nsec_until_refresh / 1000000;\n\t\t}\n\t}\n\n\tint delay = msec_until_refresh - output->max_render_time;\n\n\t// If the delay is less than 1 millisecond (which is the least we can wait)\n\t// then just render right away.\n\tif (delay < 1) {\n\t\toutput_repaint_timer_handler(output);\n\t} else {\n\t\toutput->wlr_output->frame_pending = true;\n\t\twl_event_source_timer_update(output->repaint_timer, delay);\n\t}\n\n\t// Send frame done to all visible surfaces\n\tstruct send_frame_done_data data = {0};\n\tclock_gettime(CLOCK_MONOTONIC, &data.when);\n\tdata.msec_until_refresh = msec_until_refresh;\n\tdata.output = output;\n\twlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data);\n}\n\nvoid update_output_manager_config(struct sway_server *server) {\n\tstruct wlr_output_configuration_v1 *config =\n\t\twlr_output_configuration_v1_create();\n\n\tstruct sway_output *output;\n\twl_list_for_each(output, &root->all_outputs, link) {\n\t\tif (output == root->fallback_output) {\n\t\t\tcontinue;\n\t\t}\n\t\tstruct wlr_output_configuration_head_v1 *config_head =\n\t\t\twlr_output_configuration_head_v1_create(config, output->wlr_output);\n\t\tstruct wlr_box output_box;\n\t\twlr_output_layout_get_box(root->output_layout,\n\t\t\toutput->wlr_output, &output_box);\n\t\t// We mark the output enabled when it's switched off but not disabled\n\t\tconfig_head->state.enabled = !wlr_box_empty(&output_box);\n\t\tconfig_head->state.x = output_box.x;\n\t\tconfig_head->state.y = output_box.y;\n\t}\n\n\twlr_output_manager_v1_set_configuration(server->output_manager_v1, config);\n\n\tipc_event_output();\n}\n\nstatic int timer_modeset_handle(void *data) {\n\tstruct sway_server *server = data;\n\twl_event_source_remove(server->delayed_modeset);\n\tserver->delayed_modeset = NULL;\n\n\tapply_stored_output_configs();\n\treturn 0;\n}\n\nvoid request_modeset(void) {\n\tif (server.delayed_modeset == NULL) {\n\t\tserver.delayed_modeset = wl_event_loop_add_timer(server.wl_event_loop,\n\t\t\ttimer_modeset_handle, &server);\n\t\twl_event_source_timer_update(server.delayed_modeset, 10);\n\t}\n}\n\nbool modeset_is_pending(void) {\n\treturn server.delayed_modeset != NULL;\n}\n\nvoid force_modeset(void) {\n\tif (server.delayed_modeset != NULL) {\n\t\twl_event_source_remove(server.delayed_modeset);\n\t\tserver.delayed_modeset = NULL;\n\t}\n\tapply_stored_output_configs();\n}\n\nstatic void begin_destroy(struct sway_output *output) {\n\n\twl_list_remove(&output->layout_destroy.link);\n\twl_list_remove(&output->destroy.link);\n\twl_list_remove(&output->present.link);\n\twl_list_remove(&output->frame.link);\n\twl_list_remove(&output->request_state.link);\n\n\t// Remove the scene_output first to ensure that the scene does not emit\n\t// events for this output.\n\twlr_scene_output_destroy(output->scene_output);\n\toutput->scene_output = NULL;\n\n\tif (output->enabled) {\n\t\toutput_disable(output);\n\t}\n\toutput_begin_destroy(output);\n\twl_list_remove(&output->link);\n\n\toutput->wlr_output->data = NULL;\n\toutput->wlr_output = NULL;\n\n\twl_event_source_remove(output->repaint_timer);\n\toutput->repaint_timer = NULL;\n\n\trequest_modeset();\n}\n\nstatic void handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_output *output = wl_container_of(listener, output, destroy);\n\tbegin_destroy(output);\n}\n\nstatic void handle_layout_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_output *output = wl_container_of(listener, output, layout_destroy);\n\tbegin_destroy(output);\n}\n\nstatic void handle_present(struct wl_listener *listener, void *data) {\n\tstruct sway_output *output = wl_container_of(listener, output, present);\n\tstruct wlr_output_event_present *output_event = data;\n\n\tif (!output->enabled || !output_event->presented) {\n\t\treturn;\n\t}\n\n\toutput->last_presentation = output_event->when;\n\toutput->refresh_nsec = output_event->refresh;\n}\n\nstatic void handle_request_state(struct wl_listener *listener, void *data) {\n\tstruct sway_output *output =\n\t\twl_container_of(listener, output, request_state);\n\tconst struct wlr_output_event_request_state *event = data;\n\tconst struct wlr_output_state *state = event->state;\n\n\t// Store the requested changes so that the active configuration is\n\t// consistent with the current state, and to avoid duplicate logic to apply\n\t// the changes.\n\tstruct output_config *oc = new_output_config(output->wlr_output->name);\n\tif (!oc) {\n\t\tsway_log(SWAY_ERROR, \"Allocation failed\");\n\t\treturn;\n\t}\n\n\tint committed = state->committed;\n\tif (committed & WLR_OUTPUT_STATE_MODE) {\n\t\tif (state->mode != NULL) {\n\t\t\toc->width = state->mode->width;\n\t\t\toc->height = state->mode->height;\n\t\t\toc->refresh_rate = state->mode->refresh / 1000.f;\n\t\t} else {\n\t\t\toc->width = state->custom_mode.width;\n\t\t\toc->height = state->custom_mode.height;\n\t\t\toc->refresh_rate = state->custom_mode.refresh / 1000.f;\n\t\t}\n\t\tcommitted &= ~WLR_OUTPUT_STATE_MODE;\n\t}\n\tif (committed & WLR_OUTPUT_STATE_SCALE) {\n\t\toc->scale = state->scale;\n\t\tcommitted &= ~WLR_OUTPUT_STATE_SCALE;\n\t}\n\tif (committed & WLR_OUTPUT_STATE_TRANSFORM) {\n\t\toc->transform = state->transform;\n\t\tcommitted &= ~WLR_OUTPUT_STATE_TRANSFORM;\n\t}\n\n\t// We do not expect or support any other changes here\n\tassert(committed == 0);\n\tstore_output_config(oc);\n\n\tforce_modeset();\n}\n\nstatic unsigned int last_headless_num = 0;\n\nvoid handle_new_output(struct wl_listener *listener, void *data) {\n\tstruct sway_server *server = wl_container_of(listener, server, new_output);\n\tstruct wlr_output *wlr_output = data;\n\n\tif (wlr_output == root->fallback_output->wlr_output) {\n\t\treturn;\n\t}\n\n\tif (wlr_output_is_headless(wlr_output)) {\n\t\tchar name[64];\n\t\tsnprintf(name, sizeof(name), \"HEADLESS-%u\", ++last_headless_num);\n\t\twlr_output_set_name(wlr_output, name);\n\t}\n\n\tsway_log(SWAY_DEBUG, \"New output %p: %s (non-desktop: %d)\",\n\t\t\twlr_output, wlr_output->name, wlr_output->non_desktop);\n\n\tif (wlr_output->non_desktop) {\n\t\tsway_log(SWAY_DEBUG, \"Not configuring non-desktop output\");\n\t\tstruct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output);\n#if WLR_HAS_DRM_BACKEND\n\t\tif (server->drm_lease_manager) {\n\t\t\twlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,\n\t\t\t\t\twlr_output);\n\t\t}\n#endif\n\t\tlist_add(root->non_desktop_outputs, non_desktop);\n\t\treturn;\n\t}\n\n\tif (!wlr_output_init_render(wlr_output, server->allocator,\n\t\t\tserver->renderer)) {\n\t\tsway_log(SWAY_ERROR, \"Failed to init output render\");\n\t\treturn;\n\t}\n\n\t// Create the scene output here so we're not accidentally creating one for\n\t// the fallback output\n\tstruct wlr_scene_output *scene_output =\n\t\twlr_scene_output_create(root->root_scene, wlr_output);\n\tif (!scene_output) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create a scene output\");\n\t\treturn;\n\t}\n\n\tstruct sway_output *output = output_create(wlr_output);\n\tif (!output) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create a sway output\");\n\t\twlr_scene_output_destroy(scene_output);\n\t\treturn;\n\t}\n\n\toutput->server = server;\n\toutput->scene_output = scene_output;\n\n\twl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy);\n\toutput->layout_destroy.notify = handle_layout_destroy;\n\twl_signal_add(&wlr_output->events.destroy, &output->destroy);\n\toutput->destroy.notify = handle_destroy;\n\twl_signal_add(&wlr_output->events.present, &output->present);\n\toutput->present.notify = handle_present;\n\twl_signal_add(&wlr_output->events.frame, &output->frame);\n\toutput->frame.notify = handle_frame;\n\twl_signal_add(&wlr_output->events.request_state, &output->request_state);\n\toutput->request_state.notify = handle_request_state;\n\n\toutput->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,\n\t\toutput_repaint_timer_handler, output);\n\n\tif (server->session_lock.lock) {\n\t\tsway_session_lock_add_output(server->session_lock.lock, output);\n\t}\n\n\trequest_modeset();\n}\n\nstatic struct output_config *output_config_for_config_head(\n\t\tstruct wlr_output_configuration_head_v1 *config_head) {\n\tstruct output_config *oc = new_output_config(config_head->state.output->name);\n\tif (!oc) {\n\t\treturn NULL;\n\t}\n\n\toc->enabled = config_head->state.enabled;\n\tif (!oc->enabled) {\n\t\treturn oc;\n\t}\n\n\tif (config_head->state.mode != NULL) {\n\t\tstruct wlr_output_mode *mode = config_head->state.mode;\n\t\toc->width = mode->width;\n\t\toc->height = mode->height;\n\t\toc->refresh_rate = mode->refresh / 1000.f;\n\t} else {\n\t\toc->width = config_head->state.custom_mode.width;\n\t\toc->height = config_head->state.custom_mode.height;\n\t\toc->refresh_rate =\n\t\t\tconfig_head->state.custom_mode.refresh / 1000.f;\n\t}\n\toc->x = config_head->state.x;\n\toc->y = config_head->state.y;\n\toc->transform = config_head->state.transform;\n\toc->scale = config_head->state.scale;\n\toc->adaptive_sync = config_head->state.adaptive_sync_enabled;\n\treturn oc;\n}\n\nstatic void output_manager_apply(struct sway_server *server,\n\t\tstruct wlr_output_configuration_v1 *cfg, bool test_only) {\n\tbool ok = false;\n\tsize_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads);\n\tstruct output_config **configs = calloc(configs_len, sizeof(*configs));\n\tif (!configs) {\n\t\tsway_log(SWAY_ERROR, \"Allocation failed\");\n\t\tgoto error;\n\t}\n\tsize_t start_new_configs = config->output_configs->length;\n\tfor (size_t idx = 0; idx < start_new_configs; idx++) {\n\t\tconfigs[idx] = config->output_configs->items[idx];\n\t}\n\n\tsize_t config_idx = start_new_configs;\n\tstruct wlr_output_configuration_head_v1 *config_head;\n\twl_list_for_each(config_head, &cfg->heads, link) {\n\t\t// Generate the configuration and store it as a temporary\n\t\t// config. We keep a record of it so we can remove it later.\n\t\tstruct output_config *oc = output_config_for_config_head(config_head);\n\t\tif (!oc) {\n\t\t\tsway_log(SWAY_ERROR, \"Allocation failed\");\n\t\t\tgoto error_config;\n\t\t}\n\t\tconfigs[config_idx++] = oc;\n\t}\n\n\t// Try to commit without degrade to off enabled. Note that this will fail\n\t// if any output configured for enablement fails to be enabled, even if it\n\t// was not part of the config heads we were asked to configure.\n\tok = apply_output_configs(configs, configs_len, test_only, false);\n\nerror_config:\n\tfor (size_t idx = start_new_configs; idx < configs_len; idx++) {\n\t\tstruct output_config *cfg = configs[idx];\n\t\tif (!test_only && ok) {\n\t\t\tstore_output_config(cfg);\n\t\t} else {\n\t\t\tfree_output_config(cfg);\n\t\t}\n\t}\n\tfree(configs);\n\nerror:\n\tif (ok) {\n\t\twlr_output_configuration_v1_send_succeeded(cfg);\n\t\tif (server->delayed_modeset != NULL) {\n\t\t\twl_event_source_remove(server->delayed_modeset);\n\t\t\tserver->delayed_modeset = NULL;\n\t\t}\n\t} else {\n\t\twlr_output_configuration_v1_send_failed(cfg);\n\t}\n\twlr_output_configuration_v1_destroy(cfg);\n}\n\nvoid handle_output_manager_apply(struct wl_listener *listener, void *data) {\n\tstruct sway_server *server =\n\t\twl_container_of(listener, server, output_manager_apply);\n\tstruct wlr_output_configuration_v1 *config = data;\n\n\toutput_manager_apply(server, config, false);\n}\n\nvoid handle_output_manager_test(struct wl_listener *listener, void *data) {\n\tstruct sway_server *server =\n\t\twl_container_of(listener, server, output_manager_test);\n\tstruct wlr_output_configuration_v1 *config = data;\n\n\toutput_manager_apply(server, config, true);\n}\n\nvoid handle_output_power_manager_set_mode(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct wlr_output_power_v1_set_mode_event *event = data;\n\tstruct sway_output *output = event->output->data;\n\n\tstruct output_config *oc = new_output_config(output->wlr_output->name);\n\tif (!oc) {\n\t\tsway_log(SWAY_ERROR, \"Allocation failed\");\n\t\treturn;\n\t}\n\n\tswitch (event->mode) {\n\tcase ZWLR_OUTPUT_POWER_V1_MODE_OFF:\n\t\toc->power = 0;\n\t\tbreak;\n\tcase ZWLR_OUTPUT_POWER_V1_MODE_ON:\n\t\toc->power = 1;\n\t\tbreak;\n\t}\n\tstore_output_config(oc);\n\trequest_modeset();\n}\n"
  },
  {
    "path": "sway/desktop/tearing.c",
    "content": "#include <wayland-server-core.h>\n#include <wlr/types/wlr_tearing_control_v1.h>\n#include \"sway/server.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/output.h\"\n#include \"log.h\"\n\nstruct sway_tearing_controller {\n\tstruct wlr_tearing_control_v1 *tearing_control;\n\tstruct wl_listener set_hint;\n\tstruct wl_listener destroy;\n\n\tstruct wl_list link; // sway_server::tearing_controllers\n};\n\nstatic void handle_tearing_controller_set_hint(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_tearing_controller *controller =\n\t\twl_container_of(listener, controller, set_hint);\n\n\tstruct sway_view *view = view_from_wlr_surface(\n\t\tcontroller->tearing_control->surface);\n\tif (view) {\n\t\tview->tearing_hint = controller->tearing_control->current;\n\t}\n}\n\nstatic void handle_tearing_controller_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_tearing_controller *controller =\n\t\twl_container_of(listener, controller, destroy);\n\twl_list_remove(&controller->set_hint.link);\n\twl_list_remove(&controller->destroy.link);\n\twl_list_remove(&controller->link);\n\tfree(controller);\n}\n\nvoid handle_new_tearing_hint(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_server *server =\n\t\twl_container_of(listener, server, tearing_control_new_object);\n\tstruct wlr_tearing_control_v1 *tearing_control = data;\n\n\tenum wp_tearing_control_v1_presentation_hint hint =\n\t\twlr_tearing_control_manager_v1_surface_hint_from_surface(\n\t\t\tserver->tearing_control_v1, tearing_control->surface);\n\tsway_log(SWAY_DEBUG, \"New presentation hint %d received for surface %p\",\n\t\thint, tearing_control->surface);\n\n\tstruct sway_tearing_controller *controller =\n\t\tcalloc(1, sizeof(struct sway_tearing_controller));\n\tif (!controller) {\n\t\treturn;\n\t}\n\n\tcontroller->tearing_control = tearing_control;\n\tcontroller->set_hint.notify = handle_tearing_controller_set_hint;\n\twl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);\n\tcontroller->destroy.notify = handle_tearing_controller_destroy;\n\twl_signal_add(&tearing_control->events.destroy, &controller->destroy);\n\twl_list_init(&controller->link);\n\n\twl_list_insert(&server->tearing_controllers, &controller->link);\n}\n"
  },
  {
    "path": "sway/desktop/transaction.c",
    "content": "#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <wlr/types/wlr_buffer.h>\n#include \"sway/config.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/desktop/idle_inhibit_v1.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/node.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nstruct sway_transaction {\n\tstruct wl_event_source *timer;\n\tlist_t *instructions;   // struct sway_transaction_instruction *\n\tsize_t num_waiting;\n\tsize_t num_configures;\n\tstruct timespec commit_time;\n};\n\nstruct sway_transaction_instruction {\n\tstruct sway_transaction *transaction;\n\tstruct sway_node *node;\n\tunion {\n\t\tstruct sway_output_state output_state;\n\t\tstruct sway_workspace_state workspace_state;\n\t\tstruct sway_container_state container_state;\n\t};\n\tuint32_t serial;\n\tbool server_request;\n\tbool waiting;\n};\n\nstatic struct sway_transaction *transaction_create(void) {\n\tstruct sway_transaction *transaction =\n\t\tcalloc(1, sizeof(struct sway_transaction));\n\tif (!sway_assert(transaction, \"Unable to allocate transaction\")) {\n\t\treturn NULL;\n\t}\n\ttransaction->instructions = create_list();\n\treturn transaction;\n}\n\nstatic void transaction_destroy(struct sway_transaction *transaction) {\n\t// Free instructions\n\tfor (int i = 0; i < transaction->instructions->length; ++i) {\n\t\tstruct sway_transaction_instruction *instruction =\n\t\t\ttransaction->instructions->items[i];\n\t\tstruct sway_node *node = instruction->node;\n\t\tnode->ntxnrefs--;\n\t\tif (node->instruction == instruction) {\n\t\t\tnode->instruction = NULL;\n\t\t}\n\t\tif (node->destroying && node->ntxnrefs == 0) {\n\t\t\tswitch (node->type) {\n\t\t\tcase N_ROOT:\n\t\t\t\tsway_assert(false, \"Never reached\");\n\t\t\t\tbreak;\n\t\t\tcase N_OUTPUT:\n\t\t\t\toutput_destroy(node->sway_output);\n\t\t\t\tbreak;\n\t\t\tcase N_WORKSPACE:\n\t\t\t\tworkspace_destroy(node->sway_workspace);\n\t\t\t\tbreak;\n\t\t\tcase N_CONTAINER:\n\t\t\t\tcontainer_destroy(node->sway_container);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfree(instruction);\n\t}\n\tlist_free(transaction->instructions);\n\n\tif (transaction->timer) {\n\t\twl_event_source_remove(transaction->timer);\n\t}\n\tfree(transaction);\n}\n\nstatic void copy_output_state(struct sway_output *output,\n\t\tstruct sway_transaction_instruction *instruction) {\n\tstruct sway_output_state *state = &instruction->output_state;\n\tif (state->workspaces) {\n\t\tstate->workspaces->length = 0;\n\t} else {\n\t\tstate->workspaces = create_list();\n\t}\n\tlist_cat(state->workspaces, output->workspaces);\n\n\tstate->active_workspace = output_get_active_workspace(output);\n}\n\nstatic void copy_workspace_state(struct sway_workspace *ws,\n\t\tstruct sway_transaction_instruction *instruction) {\n\tstruct sway_workspace_state *state = &instruction->workspace_state;\n\n\tstate->fullscreen = ws->fullscreen;\n\tstate->x = ws->x;\n\tstate->y = ws->y;\n\tstate->width = ws->width;\n\tstate->height = ws->height;\n\tstate->layout = ws->layout;\n\n\tstate->output = ws->output;\n\tif (state->floating) {\n\t\tstate->floating->length = 0;\n\t} else {\n\t\tstate->floating = create_list();\n\t}\n\tif (state->tiling) {\n\t\tstate->tiling->length = 0;\n\t} else {\n\t\tstate->tiling = create_list();\n\t}\n\tlist_cat(state->floating, ws->floating);\n\tlist_cat(state->tiling, ws->tiling);\n\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstate->focused = seat_get_focus(seat) == &ws->node;\n\n\t// Set focused_inactive_child to the direct tiling child\n\tstruct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws);\n\tif (focus) {\n\t\twhile (focus->pending.parent) {\n\t\t\tfocus = focus->pending.parent;\n\t\t}\n\t}\n\tstate->focused_inactive_child = focus;\n}\n\nstatic void copy_container_state(struct sway_container *container,\n\t\tstruct sway_transaction_instruction *instruction) {\n\tstruct sway_container_state *state = &instruction->container_state;\n\n\tif (state->children) {\n\t\tlist_free(state->children);\n\t}\n\n\tmemcpy(state, &container->pending, sizeof(struct sway_container_state));\n\n\tif (!container->view) {\n\t\t// We store a copy of the child list to avoid having it mutated after\n\t\t// we copy the state.\n\t\tstate->children = create_list();\n\t\tlist_cat(state->children, container->pending.children);\n\t} else {\n\t\tstate->children = NULL;\n\t}\n\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstate->focused = seat_get_focus(seat) == &container->node;\n\n\tif (!container->view) {\n\t\tstruct sway_node *focus =\n\t\t\tseat_get_active_tiling_child(seat, &container->node);\n\t\tstate->focused_inactive_child = focus ? focus->sway_container : NULL;\n\t}\n}\n\nstatic void transaction_add_node(struct sway_transaction *transaction,\n\t\tstruct sway_node *node, bool server_request) {\n\tstruct sway_transaction_instruction *instruction = NULL;\n\n\t// Check if we have an instruction for this node already, in which case we\n\t// update that instead of creating a new one.\n\tif (node->ntxnrefs > 0) {\n\t\tfor (int idx = 0; idx < transaction->instructions->length; idx++) {\n\t\t\tstruct sway_transaction_instruction *other =\n\t\t\t\ttransaction->instructions->items[idx];\n\t\t\tif (other->node == node) {\n\t\t\t\tinstruction = other;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!instruction) {\n\t\tinstruction = calloc(1, sizeof(struct sway_transaction_instruction));\n\t\tif (!sway_assert(instruction, \"Unable to allocate instruction\")) {\n\t\t\treturn;\n\t\t}\n\t\tinstruction->transaction = transaction;\n\t\tinstruction->node = node;\n\t\tinstruction->server_request = server_request;\n\n\t\tlist_add(transaction->instructions, instruction);\n\t\tnode->ntxnrefs++;\n\t} else if (server_request) {\n\t\tinstruction->server_request = true;\n\t}\n\n\tswitch (node->type) {\n\tcase N_ROOT:\n\t\tbreak;\n\tcase N_OUTPUT:\n\t\tcopy_output_state(node->sway_output, instruction);\n\t\tbreak;\n\tcase N_WORKSPACE:\n\t\tcopy_workspace_state(node->sway_workspace, instruction);\n\t\tbreak;\n\tcase N_CONTAINER:\n\t\tcopy_container_state(node->sway_container, instruction);\n\t\tbreak;\n\t}\n}\n\nstatic void apply_output_state(struct sway_output *output,\n\t\tstruct sway_output_state *state) {\n\tlist_free(output->current.workspaces);\n\tmemcpy(&output->current, state, sizeof(struct sway_output_state));\n}\n\nstatic void apply_workspace_state(struct sway_workspace *ws,\n\t\tstruct sway_workspace_state *state) {\n\tlist_free(ws->current.floating);\n\tlist_free(ws->current.tiling);\n\tmemcpy(&ws->current, state, sizeof(struct sway_workspace_state));\n}\n\nstatic void apply_container_state(struct sway_container *container,\n\t\tstruct sway_container_state *state) {\n\tstruct sway_view *view = container->view;\n\t// There are separate children lists for each instruction state, the\n\t// container's current state and the container's pending state\n\t// (ie. con->children). The list itself needs to be freed here.\n\t// Any child containers which are being deleted will be cleaned up in\n\t// transaction_destroy().\n\tlist_free(container->current.children);\n\n\tmemcpy(&container->current, state, sizeof(struct sway_container_state));\n\n\tif (view) {\n\t\tif (view->saved_surface_tree) {\n\t\t\tif (!container->node.destroying || container->node.ntxnrefs == 1) {\n\t\t\t\tview_remove_saved_buffer(view);\n\t\t\t}\n\t\t}\n\n\t\t// If the view hasn't responded to the configure, center it within\n\t\t// the container. This is important for fullscreen views which\n\t\t// refuse to resize to the size of the output.\n\t\tif (view->surface) {\n\t\t\tview_center_and_clip_surface(view);\n\t\t}\n\t}\n}\n\nstatic void arrange_title_bar(struct sway_container *con,\n\t\tint x, int y, int width, int height) {\n\tcontainer_update(con);\n\n\tbool has_title_bar = height > 0;\n\twlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar);\n\tif (!has_title_bar) {\n\t\treturn;\n\t}\n\n\twlr_scene_node_set_position(&con->title_bar.tree->node, x, y);\n\n\tcon->title_width = width;\n\tcontainer_arrange_title_bar(con);\n}\n\nstatic void disable_container(struct sway_container *con) {\n\tif (con->view) {\n\t\twlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);\n\t} else {\n\t\tfor (int i = 0; i < con->current.children->length; i++) {\n\t\t\tstruct sway_container *child = con->current.children->items[i];\n\n\t\t\twlr_scene_node_reparent(&child->scene_tree->node, con->content_tree);\n\n\t\t\tdisable_container(child);\n\t\t}\n\t}\n}\n\nstatic void arrange_container(struct sway_container *con,\n\t\tint width, int height, bool title_bar, int gaps);\n\nstatic void arrange_children(enum sway_container_layout layout, list_t *children,\n\t\tstruct sway_container *active, struct wlr_scene_tree *content,\n\t\tint width, int height, int gaps) {\n\tint title_bar_height = container_titlebar_height();\n\n\tif (layout == L_TABBED) {\n\t\tstruct sway_container *first = children->length == 1 ?\n\t\t\t((struct sway_container *)children->items[0]) : NULL;\n\t\tif (config->hide_lone_tab && first && first->view &&\n\t\t\t\tfirst->current.border != B_NORMAL) {\n\t\t\ttitle_bar_height = 0;\n\t\t}\n\n\t\tdouble w = (double) width / children->length;\n\t\tint title_offset = 0;\n\t\tfor (int i = 0; i < children->length; i++) {\n\t\t\tstruct sway_container *child = children->items[i];\n\t\t\tbool activated = child == active;\n\t\t\tint next_title_offset = round(w * i + w);\n\n\t\t\tarrange_title_bar(child, title_offset, -title_bar_height,\n\t\t\t\tnext_title_offset - title_offset, title_bar_height);\n\t\t\twlr_scene_node_set_enabled(&child->border.tree->node, activated);\n\t\t\twlr_scene_node_set_enabled(&child->scene_tree->node, true);\n\t\t\twlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height);\n\t\t\twlr_scene_node_reparent(&child->scene_tree->node, content);\n\n\t\t\tint net_height = height - title_bar_height;\n\t\t\tif (activated && width > 0 && net_height > 0) {\n\t\t\t\tarrange_container(child, width, net_height, title_bar_height == 0, 0);\n\t\t\t} else {\n\t\t\t\tdisable_container(child);\n\t\t\t}\n\n\t\t\ttitle_offset = next_title_offset;\n\t\t}\n\t} else if (layout == L_STACKED) {\n\t\tstruct sway_container *first = children->length == 1 ?\n\t\t\t((struct sway_container *)children->items[0]) : NULL;\n\t\tif (config->hide_lone_tab && first && first->view &&\n\t\t\t\tfirst->current.border != B_NORMAL) {\n\t\t\ttitle_bar_height = 0;\n\t\t}\n\n\t\tint title_height = title_bar_height * children->length;\n\n\t\tint y = 0;\n\t\tfor (int i = 0; i < children->length; i++) {\n\t\t\tstruct sway_container *child = children->items[i];\n\t\t\tbool activated = child == active;\n\n\t\t\tarrange_title_bar(child, 0, y - title_height, width, title_bar_height);\n\t\t\twlr_scene_node_set_enabled(&child->border.tree->node, activated);\n\t\t\twlr_scene_node_set_enabled(&child->scene_tree->node, true);\n\t\t\twlr_scene_node_set_position(&child->scene_tree->node, 0, title_height);\n\t\t\twlr_scene_node_reparent(&child->scene_tree->node, content);\n\n\t\t\tint net_height = height - title_height;\n\t\t\tif (activated && width > 0 && net_height > 0) {\n\t\t\t\tarrange_container(child, width, net_height, title_bar_height == 0, 0);\n\t\t\t} else {\n\t\t\t\tdisable_container(child);\n\t\t\t}\n\n\t\t\ty += title_bar_height;\n\t\t}\n\t} else if (layout == L_VERT) {\n\t\tint off = 0;\n\t\tfor (int i = 0; i < children->length; i++) {\n\t\t\tstruct sway_container *child = children->items[i];\n\t\t\tint cheight = child->current.height;\n\n\t\t\twlr_scene_node_set_enabled(&child->border.tree->node, true);\n\t\t\twlr_scene_node_set_position(&child->scene_tree->node, 0, off);\n\t\t\twlr_scene_node_reparent(&child->scene_tree->node, content);\n\t\t\tif (width > 0 && cheight > 0) {\n\t\t\t\tarrange_container(child, width, cheight, true, gaps);\n\t\t\t\toff += cheight + gaps;\n\t\t\t} else {\n\t\t\t\tdisable_container(child);\n\t\t\t}\n\t\t}\n\t} else if (layout == L_HORIZ) {\n\t\tint off = 0;\n\t\tfor (int i = 0; i < children->length; i++) {\n\t\t\tstruct sway_container *child = children->items[i];\n\t\t\tint cwidth = child->current.width;\n\n\t\t\twlr_scene_node_set_enabled(&child->border.tree->node, true);\n\t\t\twlr_scene_node_set_position(&child->scene_tree->node, off, 0);\n\t\t\twlr_scene_node_reparent(&child->scene_tree->node, content);\n\t\t\tif (cwidth > 0 && height > 0) {\n\t\t\t\tarrange_container(child, cwidth, height, true, gaps);\n\t\t\t\toff += cwidth + gaps;\n\t\t\t} else {\n\t\t\t\tdisable_container(child);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tsway_assert(false, \"unreachable\");\n\t}\n}\n\nstatic void arrange_container(struct sway_container *con,\n\t\tint width, int height, bool title_bar, int gaps) {\n\t// this container might have previously been in the scratchpad,\n\t// make sure it's enabled for viewing\n\twlr_scene_node_set_enabled(&con->scene_tree->node, true);\n\n\tif (con->output_handler) {\n\t\twlr_scene_buffer_set_dest_size(con->output_handler, width, height);\n\t}\n\n\tif (con->view) {\n\t\tint border_top = container_titlebar_height();\n\t\tint border_width = con->current.border_thickness;\n\n\t\tif (title_bar && con->current.border != B_NORMAL) {\n\t\t\twlr_scene_node_set_enabled(&con->title_bar.tree->node, false);\n\t\t\twlr_scene_node_set_enabled(&con->border.top->node, true);\n\t\t} else {\n\t\t\twlr_scene_node_set_enabled(&con->border.top->node, false);\n\t\t}\n\n\t\tif (con->current.border == B_NORMAL) {\n\t\t\tif (title_bar) {\n\t\t\t\tarrange_title_bar(con, 0, 0, width, border_top);\n\t\t\t} else {\n\t\t\t\tborder_top = 0;\n\t\t\t\t// should be handled by the parent container\n\t\t\t}\n\t\t} else if (con->current.border == B_PIXEL) {\n\t\t\tcontainer_update(con);\n\t\t\tborder_top = title_bar && con->current.border_top ? border_width : 0;\n\t\t} else if (con->current.border == B_NONE) {\n\t\t\tcontainer_update(con);\n\t\t\tborder_top = 0;\n\t\t\tborder_width = 0;\n\t\t} else if (con->current.border == B_CSD) {\n\t\t\tborder_top = 0;\n\t\t\tborder_width = 0;\n\t\t} else {\n\t\t\tsway_assert(false, \"unreachable\");\n\t\t}\n\n\t\tint border_bottom = con->current.border_bottom ? border_width : 0;\n\t\tint border_left = con->current.border_left ? border_width : 0;\n\t\tint border_right = con->current.border_right ? border_width : 0;\n\t\tint vert_border_height = MAX(0, height - border_top - border_bottom);\n\n\t\twlr_scene_rect_set_size(con->border.top, width, border_top);\n\t\twlr_scene_rect_set_size(con->border.bottom, width, border_bottom);\n\t\twlr_scene_rect_set_size(con->border.left,\n\t\t\tborder_left, vert_border_height);\n\t\twlr_scene_rect_set_size(con->border.right,\n\t\t\tborder_right, vert_border_height);\n\n\t\twlr_scene_node_set_position(&con->border.top->node, 0, 0);\n\t\twlr_scene_node_set_position(&con->border.bottom->node,\n\t\t\t0, height - border_bottom);\n\t\twlr_scene_node_set_position(&con->border.left->node,\n\t\t\t0, border_top);\n\t\twlr_scene_node_set_position(&con->border.right->node,\n\t\t\twidth - border_right, border_top);\n\n\t\t// make sure to reparent, it's possible that the client just came out of\n\t\t// fullscreen mode where the parent of the surface is not the container\n\t\twlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);\n\t\twlr_scene_node_set_position(&con->view->scene_tree->node,\n\t\t\tborder_left, border_top);\n\t} else {\n\t\t// make sure to disable the title bar if the parent is not managing it\n\t\tif (title_bar) {\n\t\t\twlr_scene_node_set_enabled(&con->title_bar.tree->node, false);\n\t\t}\n\n\t\tarrange_children(con->current.layout, con->current.children,\n\t\t\tcon->current.focused_inactive_child, con->content_tree,\n\t\t\twidth, height, gaps);\n\t}\n}\n\nstatic int container_get_gaps(struct sway_container *con) {\n\tstruct sway_workspace *ws = con->current.workspace;\n\tstruct sway_container *temp = con;\n\twhile (temp) {\n\t\tenum sway_container_layout layout;\n\t\tif (temp->current.parent) {\n\t\t\tlayout = temp->current.parent->current.layout;\n\t\t} else {\n\t\t\tlayout = ws->current.layout;\n\t\t}\n\t\tif (layout == L_TABBED || layout == L_STACKED) {\n\t\t\treturn 0;\n\t\t}\n\t\ttemp = temp->pending.parent;\n\t}\n\treturn ws->gaps_inner;\n}\n\nstatic void arrange_fullscreen(struct wlr_scene_tree *tree,\n\t\tstruct sway_container *fs, struct sway_workspace *ws,\n\t\tint width, int height) {\n\tstruct wlr_scene_node *fs_node;\n\tif (fs->view) {\n\t\tfs_node = &fs->view->scene_tree->node;\n\n\t\t// if we only care about the view, disable any decorations\n\t\twlr_scene_node_set_enabled(&fs->scene_tree->node, false);\n\t} else {\n\t\tfs_node = &fs->scene_tree->node;\n\t\tarrange_container(fs, width, height, true, container_get_gaps(fs));\n\t}\n\n\twlr_scene_node_reparent(fs_node, tree);\n\twlr_scene_node_lower_to_bottom(fs_node);\n\twlr_scene_node_set_position(fs_node, 0, 0);\n}\n\nstatic void arrange_workspace_floating(struct sway_workspace *ws) {\n\tfor (int i = 0; i < ws->current.floating->length; i++) {\n\t\tstruct sway_container *floater = ws->current.floating->items[i];\n\t\tstruct wlr_scene_tree *layer = root->layers.floating;\n\n\t\tif (floater->current.fullscreen_mode != FULLSCREEN_NONE) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (root->fullscreen_global) {\n\t\t\tif (container_is_transient_for(floater, root->fullscreen_global)) {\n\t\t\t\tlayer = root->layers.fullscreen_global;\n\t\t\t}\n\t\t} else {\n\t\t\tfor (int i = 0; i < root->outputs->length; i++) {\n\t\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\t\tstruct sway_workspace *active = output->current.active_workspace;\n\n\t\t\t\tif (active && active->fullscreen &&\n\t\t\t\t\t\tcontainer_is_transient_for(floater, active->fullscreen)) {\n\t\t\t\t\tlayer = root->layers.fullscreen;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\twlr_scene_node_reparent(&floater->scene_tree->node, layer);\n\t\twlr_scene_node_set_position(&floater->scene_tree->node,\n\t\t\tfloater->current.x, floater->current.y);\n\t\twlr_scene_node_set_enabled(&floater->scene_tree->node, true);\n\t\twlr_scene_node_set_enabled(&floater->border.tree->node, true);\n\n\t\tarrange_container(floater, floater->current.width, floater->current.height,\n\t\t\ttrue, ws->gaps_inner);\n\t}\n}\n\nstatic void arrange_workspace_tiling(struct sway_workspace *ws,\n\t\tint width, int height) {\n\tarrange_children(ws->current.layout, ws->current.tiling,\n\t\tws->current.focused_inactive_child, ws->layers.tiling,\n\t\twidth, height, ws->gaps_inner);\n}\n\nstatic void disable_workspace(struct sway_workspace *ws) {\n\t// if any containers were just moved to a disabled workspace it will\n\t// have the parent of the old workspace. Move the workspace so that it won't\n\t// be shown.\n\tfor (int i = 0; i < ws->current.tiling->length; i++) {\n\t\tstruct sway_container *child = ws->current.tiling->items[i];\n\n\t\twlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling);\n\t\tdisable_container(child);\n\t}\n\n\tfor (int i = 0; i < ws->current.floating->length; i++) {\n\t\tstruct sway_container *floater = ws->current.floating->items[i];\n\t\twlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);\n\t\tdisable_container(floater);\n\t\twlr_scene_node_set_enabled(&floater->scene_tree->node, false);\n\t}\n}\n\nstatic void arrange_output(struct sway_output *output, int width, int height) {\n\tfor (int i = 0; i < output->current.workspaces->length; i++) {\n\t\tstruct sway_workspace *child = output->current.workspaces->items[i];\n\n\t\tbool activated = output->current.active_workspace == child && output->wlr_output->enabled;\n\n\t\twlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling);\n\t\twlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen);\n\n\t\tfor (int i = 0; i < child->current.floating->length; i++) {\n\t\t\tstruct sway_container *floater = child->current.floating->items[i];\n\t\t\twlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);\n\t\t\twlr_scene_node_set_enabled(&floater->scene_tree->node, activated);\n\t\t}\n\n\t\tif (activated) {\n\t\t\tstruct sway_container *fs = child->current.fullscreen;\n\t\t\twlr_scene_node_set_enabled(&child->layers.tiling->node, !fs);\n\t\t\twlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs);\n\n\t\t\twlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs);\n\t\t\twlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs);\n\t\t\twlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs);\n\n\t\t\tif (fs) {\n\t\t\t\tdisable_workspace(child);\n\n\t\t\t\twlr_scene_rect_set_size(output->fullscreen_background, width, height);\n\n\t\t\t\tarrange_workspace_floating(child);\n\t\t\t\tarrange_fullscreen(child->layers.fullscreen, fs, child,\n\t\t\t\t\twidth, height);\n\t\t\t} else {\n\t\t\t\tstruct wlr_box *area = &output->usable_area;\n\t\t\t\tstruct side_gaps *gaps = &child->current_gaps;\n\n\t\t\t\twlr_scene_node_set_position(&child->layers.tiling->node,\n\t\t\t\t\tgaps->left + area->x, gaps->top + area->y);\n\n\t\t\t\tarrange_workspace_tiling(child,\n\t\t\t\t\tarea->width - gaps->left - gaps->right,\n\t\t\t\t\tarea->height - gaps->top - gaps->bottom);\n\t\t\t\tarrange_workspace_floating(child);\n\t\t\t}\n\t\t} else {\n\t\t\twlr_scene_node_set_enabled(&child->layers.tiling->node, false);\n\t\t\twlr_scene_node_set_enabled(&child->layers.fullscreen->node, false);\n\n\t\t\tdisable_workspace(child);\n\t\t}\n\t}\n}\n\nvoid arrange_popups(struct wlr_scene_tree *popups) {\n\tstruct wlr_scene_node *node;\n\twl_list_for_each(node, &popups->children, link) {\n\t\tstruct sway_popup_desc *popup = scene_descriptor_try_get(node,\n\t\t\tSWAY_SCENE_DESC_POPUP);\n\n\t\tif (popup) {\n\t\t\tint lx, ly;\n\t\t\twlr_scene_node_coords(popup->relative, &lx, &ly);\n\t\t\twlr_scene_node_set_position(node, lx, ly);\n\t\t}\n\t}\n}\n\nstatic void arrange_root(struct sway_root *root) {\n\tstruct sway_container *fs = root->fullscreen_global;\n\n\twlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs);\n\twlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs);\n\twlr_scene_node_set_enabled(&root->layers.tiling->node, !fs);\n\twlr_scene_node_set_enabled(&root->layers.floating->node, !fs);\n\twlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs);\n\twlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs);\n\n\t// hide all contents in the scratchpad\n\tfor (int i = 0; i < root->scratchpad->length; i++) {\n\t\tstruct sway_container *con = root->scratchpad->items[i];\n\n\t\t// When a container is moved to a scratchpad, it's possible that it\n\t\t// was moved into a floating container as part of the same transaction.\n\t\t// In this case, we need to make sure we reparent all the container's\n\t\t// children so that disabling the container will disable all descendants.\n\t\tif (!con->view) for (int ii = 0; ii < con->current.children->length; ii++) {\n\t\t\tstruct sway_container *child = con->current.children->items[ii];\n\t\t\twlr_scene_node_reparent(&child->scene_tree->node, con->content_tree);\n\t\t}\n\n\t\twlr_scene_node_set_enabled(&con->scene_tree->node, false);\n\t}\n\n\tif (fs) {\n\t\tfor (int i = 0; i < root->outputs->length; i++) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tstruct sway_workspace *ws = output->current.active_workspace;\n\n\t\t\twlr_scene_output_set_position(output->scene_output, output->lx, output->ly);\n\n\t\t\t// disable all workspaces to get to a known state\n\t\t\tfor (int j = 0; j < output->current.workspaces->length; j++) {\n\t\t\t\tstruct sway_workspace *workspace = output->current.workspaces->items[j];\n\t\t\t\tdisable_workspace(workspace);\n\t\t\t}\n\n\t\t\t// arrange the active workspace\n\t\t\tif (ws) {\n\t\t\t\tarrange_workspace_floating(ws);\n\t\t\t}\n\t\t}\n\n\t\tarrange_fullscreen(root->layers.fullscreen_global, fs, NULL,\n\t\t\troot->width, root->height);\n\t} else {\n\t\tfor (int i = 0; i < root->outputs->length; i++) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\n\t\t\twlr_scene_output_set_position(output->scene_output, output->lx, output->ly);\n\n\t\t\twlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background);\n\t\t\twlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom);\n\t\t\twlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling);\n\t\t\twlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top);\n\t\t\twlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay);\n\t\t\twlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen);\n\t\t\twlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock);\n\n\t\t\twlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly);\n\t\t\twlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly);\n\t\t\twlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly);\n\t\t\twlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly);\n\t\t\twlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly);\n\t\t\twlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly);\n\t\t\twlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly);\n\n\t\t\tarrange_output(output, output->width, output->height);\n\t\t}\n\t}\n\n\tarrange_popups(root->layers.popup);\n}\n\n/**\n * Apply a transaction to the \"current\" state of the tree.\n */\nstatic void transaction_apply(struct sway_transaction *transaction) {\n\tsway_log(SWAY_DEBUG, \"Applying transaction %p\", transaction);\n\tif (debug.txn_timings) {\n\t\tstruct timespec now;\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\t\tstruct timespec *commit = &transaction->commit_time;\n\t\tfloat ms = (now.tv_sec - commit->tv_sec) * 1000 +\n\t\t\t(now.tv_nsec - commit->tv_nsec) / 1000000.0;\n\t\tsway_log(SWAY_DEBUG, \"Transaction %p: %.1fms waiting \"\n\t\t\t\t\"(%.1f frames if 60Hz)\", transaction, ms, ms / (1000.0f / 60));\n\t}\n\n\t// Apply the instruction state to the node's current state\n\tfor (int i = 0; i < transaction->instructions->length; ++i) {\n\t\tstruct sway_transaction_instruction *instruction =\n\t\t\ttransaction->instructions->items[i];\n\t\tstruct sway_node *node = instruction->node;\n\n\t\tswitch (node->type) {\n\t\tcase N_ROOT:\n\t\t\tbreak;\n\t\tcase N_OUTPUT:\n\t\t\tapply_output_state(node->sway_output, &instruction->output_state);\n\t\t\tbreak;\n\t\tcase N_WORKSPACE:\n\t\t\tapply_workspace_state(node->sway_workspace,\n\t\t\t\t\t&instruction->workspace_state);\n\t\t\tbreak;\n\t\tcase N_CONTAINER:\n\t\t\tapply_container_state(node->sway_container,\n\t\t\t\t\t&instruction->container_state);\n\t\t\tbreak;\n\t\t}\n\n\t\tnode->instruction = NULL;\n\t}\n}\n\nstatic void transaction_commit_pending(void);\n\nstatic void transaction_progress(void) {\n\tif (!server.queued_transaction) {\n\t\treturn;\n\t}\n\tif (server.queued_transaction->num_waiting > 0) {\n\t\treturn;\n\t}\n\ttransaction_apply(server.queued_transaction);\n\tarrange_root(root);\n\tcursor_rebase_all();\n\ttransaction_destroy(server.queued_transaction);\n\tserver.queued_transaction = NULL;\n\n\tif (!server.pending_transaction) {\n\t\tsway_idle_inhibit_v1_check_active();\n\t\treturn;\n\t}\n\n\ttransaction_commit_pending();\n}\n\nstatic int handle_timeout(void *data) {\n\tstruct sway_transaction *transaction = data;\n\tsway_log(SWAY_DEBUG, \"Transaction %p timed out (%zi waiting)\",\n\t\t\ttransaction, transaction->num_waiting);\n\ttransaction->num_waiting = 0;\n\ttransaction_progress();\n\treturn 0;\n}\n\nstatic bool should_configure(struct sway_node *node,\n\t\tstruct sway_transaction_instruction *instruction) {\n\tif (!node_is_view(node)) {\n\t\treturn false;\n\t}\n\tif (node->destroying) {\n\t\treturn false;\n\t}\n\tif (!instruction->server_request) {\n\t\treturn false;\n\t}\n\tstruct sway_container_state *cstate = &node->sway_container->current;\n\tstruct sway_container_state *istate = &instruction->container_state;\n#if WLR_HAS_XWAYLAND\n\t// Xwayland views are position-aware and need to be reconfigured\n\t// when their position changes.\n\tif (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {\n\t\t// Sway logical coordinates are doubles, but they get truncated to\n\t\t// integers when sent to Xwayland through `xcb_configure_window`.\n\t\t// X11 apps will not respond to duplicate configure requests (from their\n\t\t// truncated point of view) and cause transactions to time out.\n\t\tif ((int)cstate->content_x != (int)istate->content_x ||\n\t\t\t\t(int)cstate->content_y != (int)istate->content_y) {\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\tif (cstate->content_width == istate->content_width &&\n\t\t\tcstate->content_height == istate->content_height) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic void transaction_commit(struct sway_transaction *transaction) {\n\tsway_log(SWAY_DEBUG, \"Transaction %p committing with %i instructions\",\n\t\t\ttransaction, transaction->instructions->length);\n\ttransaction->num_waiting = 0;\n\tfor (int i = 0; i < transaction->instructions->length; ++i) {\n\t\tstruct sway_transaction_instruction *instruction =\n\t\t\ttransaction->instructions->items[i];\n\t\tstruct sway_node *node = instruction->node;\n\t\tbool hidden = node_is_view(node) && !node->destroying &&\n\t\t\t!view_is_visible(node->sway_container->view);\n\t\tif (should_configure(node, instruction)) {\n\t\t\tinstruction->serial = view_configure(node->sway_container->view,\n\t\t\t\t\tinstruction->container_state.content_x,\n\t\t\t\t\tinstruction->container_state.content_y,\n\t\t\t\t\tinstruction->container_state.content_width,\n\t\t\t\t\tinstruction->container_state.content_height);\n\t\t\tif (!hidden) {\n\t\t\t\tinstruction->waiting = true;\n\t\t\t\t++transaction->num_waiting;\n\t\t\t}\n\n\t\t\tview_send_frame_done(node->sway_container->view);\n\t\t}\n\t\tif (!hidden && node_is_view(node) &&\n\t\t\t\t!node->sway_container->view->saved_surface_tree) {\n\t\t\tview_save_buffer(node->sway_container->view);\n\t\t}\n\t\tnode->instruction = instruction;\n\t}\n\ttransaction->num_configures = transaction->num_waiting;\n\tif (debug.txn_timings) {\n\t\tclock_gettime(CLOCK_MONOTONIC, &transaction->commit_time);\n\t}\n\tif (debug.noatomic) {\n\t\ttransaction->num_waiting = 0;\n\t} else if (debug.txn_wait) {\n\t\t// Force the transaction to time out even if all views are ready.\n\t\t// We do this by inflating the waiting counter.\n\t\ttransaction->num_waiting += 1000000;\n\t}\n\n\tif (transaction->num_waiting) {\n\t\t// Set up a timer which the views must respond within\n\t\ttransaction->timer = wl_event_loop_add_timer(server.wl_event_loop,\n\t\t\t\thandle_timeout, transaction);\n\t\tif (transaction->timer) {\n\t\t\twl_event_source_timer_update(transaction->timer,\n\t\t\t\t\tserver.txn_timeout_ms);\n\t\t} else {\n\t\t\tsway_log_errno(SWAY_ERROR, \"Unable to create transaction timer \"\n\t\t\t\t\t\"(some imperfect frames might be rendered)\");\n\t\t\ttransaction->num_waiting = 0;\n\t\t}\n\t}\n}\n\nstatic void transaction_commit_pending(void) {\n\tif (server.queued_transaction) {\n\t\treturn;\n\t}\n\tstruct sway_transaction *transaction = server.pending_transaction;\n\tserver.pending_transaction = NULL;\n\tserver.queued_transaction = transaction;\n\ttransaction_commit(transaction);\n\ttransaction_progress();\n}\n\nstatic void set_instruction_ready(\n\t\tstruct sway_transaction_instruction *instruction) {\n\tstruct sway_transaction *transaction = instruction->transaction;\n\n\tif (debug.txn_timings) {\n\t\tstruct timespec now;\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\t\tstruct timespec *start = &transaction->commit_time;\n\t\tfloat ms = (now.tv_sec - start->tv_sec) * 1000 +\n\t\t\t(now.tv_nsec - start->tv_nsec) / 1000000.0;\n\t\tsway_log(SWAY_DEBUG, \"Transaction %p: %zi/%zi ready in %.1fms (%s)\",\n\t\t\t\ttransaction,\n\t\t\t\ttransaction->num_configures - transaction->num_waiting + 1,\n\t\t\t\ttransaction->num_configures, ms,\n\t\t\t\tinstruction->node->sway_container->title);\n\t}\n\n\t// If the transaction has timed out then its num_waiting will be 0 already.\n\tif (instruction->waiting && transaction->num_waiting > 0 &&\n\t\t\t--transaction->num_waiting == 0) {\n\t\tsway_log(SWAY_DEBUG, \"Transaction %p is ready\", transaction);\n\t\twl_event_source_timer_update(transaction->timer, 0);\n\t}\n\n\tinstruction->node->instruction = NULL;\n\ttransaction_progress();\n}\n\nbool transaction_notify_view_ready_by_serial(struct sway_view *view,\n\t\tuint32_t serial) {\n\tstruct sway_transaction_instruction *instruction =\n\t\tview->container->node.instruction;\n\tif (instruction != NULL && instruction->serial == serial) {\n\t\tset_instruction_ready(instruction);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool transaction_notify_view_ready_by_geometry(struct sway_view *view,\n\t\tdouble x, double y, int width, int height) {\n\tstruct sway_transaction_instruction *instruction =\n\t\tview->container->node.instruction;\n\tif (instruction != NULL &&\n\t\t\t(int)instruction->container_state.content_x == (int)x &&\n\t\t\t(int)instruction->container_state.content_y == (int)y &&\n\t\t\tinstruction->container_state.content_width == width &&\n\t\t\tinstruction->container_state.content_height == height) {\n\t\tset_instruction_ready(instruction);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void _transaction_commit_dirty(bool server_request) {\n\tif (!server.dirty_nodes->length) {\n\t\treturn;\n\t}\n\n\tif (!server.pending_transaction) {\n\t\tserver.pending_transaction = transaction_create();\n\t\tif (!server.pending_transaction) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (int i = 0; i < server.dirty_nodes->length; ++i) {\n\t\tstruct sway_node *node = server.dirty_nodes->items[i];\n\t\ttransaction_add_node(server.pending_transaction, node, server_request);\n\t\tnode->dirty = false;\n\t}\n\tserver.dirty_nodes->length = 0;\n\n\ttransaction_commit_pending();\n}\n\nvoid transaction_commit_dirty(void) {\n\t_transaction_commit_dirty(true);\n}\n\nvoid transaction_commit_dirty_client(void) {\n\t_transaction_commit_dirty(false);\n}\n"
  },
  {
    "path": "sway/desktop/xdg_shell.c",
    "content": "#include <float.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <wayland-server-core.h>\n#include <wlr/types/wlr_xdg_shell.h>\n#include <wlr/types/wlr_xdg_toplevel_tag_v1.h>\n#include <wlr/util/edges.h>\n#include \"log.h\"\n#include \"sway/decoration.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/xdg_decoration.h\"\n\nstatic struct sway_xdg_popup *popup_create(\n\tstruct wlr_xdg_popup *wlr_popup, struct sway_view *view,\n\tstruct wlr_scene_tree *parent, struct wlr_scene_tree *image_capture_parent);\n\nstatic void popup_handle_new_popup(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_popup *popup =\n\t\twl_container_of(listener, popup, new_popup);\n\tstruct wlr_xdg_popup *wlr_popup = data;\n\tpopup_create(wlr_popup, popup->view, popup->xdg_surface_tree, popup->image_capture_tree);\n}\n\nstatic void popup_handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);\n\n\twl_list_remove(&popup->new_popup.link);\n\twl_list_remove(&popup->destroy.link);\n\twl_list_remove(&popup->surface_commit.link);\n\twl_list_remove(&popup->reposition.link);\n\twlr_scene_node_destroy(&popup->scene_tree->node);\n\tfree(popup);\n}\n\nstatic void popup_unconstrain(struct sway_xdg_popup *popup) {\n\tstruct sway_view *view = popup->view;\n\tstruct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;\n\n\tstruct sway_workspace *workspace = view->container->pending.workspace;\n\tif (!workspace) {\n\t\t// is null if in the scratchpad\n\t\treturn;\n\t}\n\n\tstruct sway_output *output = workspace->output;\n\n\t// the output box expressed in the coordinate system of the toplevel parent\n\t// of the popup\n\tstruct wlr_box output_toplevel_sx_box = {\n\t\t.x = output->lx - view->container->pending.content_x + view->geometry.x,\n\t\t.y = output->ly - view->container->pending.content_y + view->geometry.y,\n\t\t.width = output->width,\n\t\t.height = output->height,\n\t};\n\n\twlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);\n}\n\nstatic void popup_handle_surface_commit(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit);\n\tif (popup->wlr_xdg_popup->base->initial_commit) {\n\t\tpopup_unconstrain(popup);\n\t}\n}\n\nstatic void popup_handle_reposition(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition);\n\tpopup_unconstrain(popup);\n}\n\nstatic struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,\n\t\tstruct sway_view *view, struct wlr_scene_tree *parent,\n\t\tstruct wlr_scene_tree *image_capture_parent) {\n\tstruct wlr_xdg_surface *xdg_surface = wlr_popup->base;\n\n\tstruct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));\n\tif (!popup) {\n\t\treturn NULL;\n\t}\n\n\tpopup->wlr_xdg_popup = wlr_popup;\n\tpopup->view = view;\n\n\tpopup->scene_tree = wlr_scene_tree_create(parent);\n\tif (!popup->scene_tree) {\n\t\tfree(popup);\n\t\treturn NULL;\n\t}\n\n\tpopup->xdg_surface_tree = wlr_scene_xdg_surface_create(\n\t\tpopup->scene_tree, xdg_surface);\n\tif (!popup->xdg_surface_tree) {\n\t\twlr_scene_node_destroy(&popup->scene_tree->node);\n\t\tfree(popup);\n\t\treturn NULL;\n\t}\n\n\tpopup->desc.relative = &view->content_tree->node;\n\tpopup->desc.view = view;\n\n\tif (!scene_descriptor_assign(&popup->scene_tree->node,\n\t\t\tSWAY_SCENE_DESC_POPUP, &popup->desc)) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate a popup scene descriptor\");\n\t\twlr_scene_node_destroy(&popup->scene_tree->node);\n\t\tfree(popup);\n\t\treturn NULL;\n\t}\n\n\tpopup->image_capture_tree = wlr_scene_xdg_surface_create(image_capture_parent, xdg_surface);\n\tif (popup->image_capture_tree == NULL) {\n\t\treturn NULL;\n\t}\n\n\tpopup->wlr_xdg_popup = xdg_surface->popup;\n\tstruct sway_xdg_shell_view *shell_view =\n\t\twl_container_of(view, shell_view, view);\n\txdg_surface->data = shell_view;\n\n\twl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);\n\tpopup->surface_commit.notify = popup_handle_surface_commit;\n\twl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);\n\tpopup->new_popup.notify = popup_handle_new_popup;\n\twl_signal_add(&wlr_popup->events.reposition, &popup->reposition);\n\tpopup->reposition.notify = popup_handle_reposition;\n\twl_signal_add(&wlr_popup->events.destroy, &popup->destroy);\n\tpopup->destroy.notify = popup_handle_destroy;\n\n\treturn popup;\n}\n\nstatic struct sway_xdg_shell_view *xdg_shell_view_from_view(\n\t\tstruct sway_view *view) {\n\tif (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,\n\t\t\t\"Expected xdg_shell view\")) {\n\t\treturn NULL;\n\t}\n\treturn (struct sway_xdg_shell_view *)view;\n}\n\nstatic void get_constraints(struct sway_view *view, double *min_width,\n\t\tdouble *max_width, double *min_height, double *max_height) {\n\tstruct wlr_xdg_toplevel_state *state =\n\t\t&view->wlr_xdg_toplevel->current;\n\t*min_width = state->min_width > 0 ? state->min_width : DBL_MIN;\n\t*max_width = state->max_width > 0 ? state->max_width : DBL_MAX;\n\t*min_height = state->min_height > 0 ? state->min_height : DBL_MIN;\n\t*max_height = state->max_height > 0 ? state->max_height : DBL_MAX;\n}\n\nstatic const char *get_string_prop(struct sway_view *view,\n\t\tenum sway_view_prop prop) {\n\tstruct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);\n\tif (xdg_shell_view == NULL) {\n\t\treturn NULL;\n\t}\n\tswitch (prop) {\n\tcase VIEW_PROP_TITLE:\n\t\treturn view->wlr_xdg_toplevel->title;\n\tcase VIEW_PROP_APP_ID:\n\t\treturn view->wlr_xdg_toplevel->app_id;\n\tcase VIEW_PROP_TAG:\n\t\treturn xdg_shell_view->tag;\n\tdefault:\n\t\treturn NULL;\n\t}\n}\n\nstatic uint32_t configure(struct sway_view *view, double lx, double ly,\n\t\tint width, int height) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\txdg_shell_view_from_view(view);\n\tif (xdg_shell_view == NULL) {\n\t\treturn 0;\n\t}\n\treturn wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel,\n\t\twidth, height);\n}\n\nstatic void set_activated(struct sway_view *view, bool activated) {\n\tif (xdg_shell_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\twlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated);\n}\n\nstatic void set_tiled(struct sway_view *view, bool tiled) {\n\tif (xdg_shell_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\tif (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=\n\t\t\tXDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {\n\t\tenum wlr_edges edges = WLR_EDGE_NONE;\n\t\tif (tiled) {\n\t\t\tedges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |\n\t\t\t\t\tWLR_EDGE_BOTTOM;\n\t\t}\n\t\twlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);\n\t} else {\n\t\t// The version is too low for the tiled state; configure as maximized instead\n\t\t// to stop the client from drawing decorations outside of the toplevel geometry.\n\t\twlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled);\n\t}\n}\n\nstatic void set_fullscreen(struct sway_view *view, bool fullscreen) {\n\tif (xdg_shell_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\twlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen);\n}\n\nstatic void set_resizing(struct sway_view *view, bool resizing) {\n\tif (xdg_shell_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\twlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing);\n}\n\nstatic bool wants_floating(struct sway_view *view) {\n\tstruct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;\n\tstruct wlr_xdg_toplevel_state *state = &toplevel->current;\n\treturn (state->min_width != 0 && state->min_height != 0\n\t\t&& (state->min_width == state->max_width\n\t\t|| state->min_height == state->max_height))\n\t\t|| toplevel->parent;\n}\n\nstatic bool is_transient_for(struct sway_view *child,\n\t\tstruct sway_view *ancestor) {\n\tif (xdg_shell_view_from_view(child) == NULL) {\n\t\treturn false;\n\t}\n\tstruct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel;\n\twhile (toplevel) {\n\t\tif (toplevel->parent == ancestor->wlr_xdg_toplevel) {\n\t\t\treturn true;\n\t\t}\n\t\ttoplevel = toplevel->parent;\n\t}\n\treturn false;\n}\n\nstatic void _close(struct sway_view *view) {\n\tif (xdg_shell_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\twlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel);\n}\n\nstatic void close_popups(struct sway_view *view) {\n\tstruct wlr_xdg_popup *popup, *tmp;\n\twl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) {\n\t\twlr_xdg_popup_destroy(popup);\n\t}\n}\n\nstatic void destroy(struct sway_view *view) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\txdg_shell_view_from_view(view);\n\tif (xdg_shell_view == NULL) {\n\t\treturn;\n\t}\n\tfree(xdg_shell_view->tag);\n\tfree(xdg_shell_view);\n}\n\nstatic const struct sway_view_impl view_impl = {\n\t.get_constraints = get_constraints,\n\t.get_string_prop = get_string_prop,\n\t.configure = configure,\n\t.set_activated = set_activated,\n\t.set_tiled = set_tiled,\n\t.set_fullscreen = set_fullscreen,\n\t.set_resizing = set_resizing,\n\t.wants_floating = wants_floating,\n\t.is_transient_for = is_transient_for,\n\t.close = _close,\n\t.close_popups = close_popups,\n\t.destroy = destroy,\n};\n\nstatic void handle_commit(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, commit);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\tstruct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;\n\n\tif (xdg_surface->initial_commit) {\n\t\tif (view->xdg_decoration != NULL) {\n\t\t\tset_xdg_decoration_mode(view->xdg_decoration);\n\t\t}\n\t\t// XXX: https://github.com/swaywm/sway/issues/2176\n\t\twlr_xdg_surface_schedule_configure(xdg_surface);\n\t\twlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel,\n\t\t\tWLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);\n\t\t// TODO: wlr_xdg_toplevel_set_bounds()\n\t\treturn;\n\t}\n\n\tif (!xdg_surface->surface->mapped) {\n\t\treturn;\n\t}\n\n\tstruct wlr_box *new_geo = &xdg_surface->geometry;\n\tbool new_size = new_geo->width != view->geometry.width ||\n\t\t\tnew_geo->height != view->geometry.height ||\n\t\t\tnew_geo->x != view->geometry.x ||\n\t\t\tnew_geo->y != view->geometry.y;\n\n\tif (new_size) {\n\t\t// The client changed its surface size in this commit. For floating\n\t\t// containers, we resize the container to match. For tiling containers,\n\t\t// we only recenter the surface.\n\t\tmemcpy(&view->geometry, new_geo, sizeof(struct wlr_box));\n\t\tif (container_is_floating(view->container)) {\n\t\t\tview_update_size(view);\n\t\t\t// Only set the toplevel size the current container actually has a size.\n\t\t\tif (view->container->current.width) {\n\t\t\t\twlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width,\n\t\t\t\t\tview->geometry.height);\n\t\t\t}\n\t\t\ttransaction_commit_dirty_client();\n\t\t}\n\n\t\tview_center_and_clip_surface(view);\n\t}\n\n\tif (view->container->node.instruction) {\n\t\tbool successful = transaction_notify_view_ready_by_serial(view,\n\t\t\t\txdg_surface->current.configure_serial);\n\n\t\t// If we saved the view and this commit isn't what we're looking for\n\t\t// that means the user will never actually see the buffers submitted to\n\t\t// us here. Just send frame done events to these surfaces so they can\n\t\t// commit another time for us.\n\t\tif (view->saved_surface_tree && !successful) {\n\t\t\tview_send_frame_done(view);\n\t\t}\n\t}\n}\n\nstatic void handle_set_title(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, set_title);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\tview_update_title(view, false);\n\tview_execute_criteria(view);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_set_app_id(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, set_app_id);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\tview_update_app_id(view);\n\tview_execute_criteria(view);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_new_popup(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, new_popup);\n\tstruct wlr_xdg_popup *wlr_popup = data;\n\n\tstruct sway_xdg_popup *popup = popup_create(wlr_popup,\n\t\t&xdg_shell_view->view, root->layers.popup, xdg_shell_view->image_capture_tree);\n\tif (!popup) {\n\t\treturn;\n\t}\n\n\tint lx, ly;\n\twlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly);\n\twlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);\n}\n\nstatic void handle_request_maximize(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, request_maximize);\n\tstruct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;\n\tif (!toplevel->base->surface->mapped) {\n\t\treturn;\n\t}\n\twlr_xdg_surface_schedule_configure(toplevel->base);\n}\n\nstatic void handle_request_fullscreen(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, request_fullscreen);\n\tstruct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;\n\tstruct sway_view *view = &xdg_shell_view->view;\n\n\tif (!toplevel->base->surface->mapped) {\n\t\treturn;\n\t}\n\n\tstruct sway_container *container = view->container;\n\tstruct wlr_xdg_toplevel_requested *req = &toplevel->requested;\n\tif (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) {\n\t\tstruct sway_output *output = req->fullscreen_output->data;\n\t\tstruct sway_workspace *ws = output_get_active_workspace(output);\n\t\tif (ws && !container_is_scratchpad_hidden(container) &&\n\t\t\t\tcontainer->pending.workspace != ws) {\n\t\t\tif (container_is_floating(container)) {\n\t\t\t\tworkspace_add_floating(ws, container);\n\t\t\t} else {\n\t\t\t\tcontainer = workspace_add_tiling(ws, container);\n\t\t\t}\n\t\t}\n\t}\n\n\tcontainer_set_fullscreen(container, req->fullscreen);\n\n\tarrange_root();\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_request_move(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, request_move);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\tif (!container_is_floating(view->container) ||\n\t\t\tview->container->pending.fullscreen_mode) {\n\t\treturn;\n\t}\n\tstruct wlr_xdg_toplevel_move_event *e = data;\n\tstruct sway_seat *seat = e->seat->seat->data;\n\tif (e->serial == seat->last_button_serial) {\n\t\tseatop_begin_move_floating(seat, view->container);\n\t}\n}\n\nstatic void handle_request_resize(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, request_resize);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\tif (!container_is_floating(view->container)) {\n\t\treturn;\n\t}\n\tstruct wlr_xdg_toplevel_resize_event *e = data;\n\tstruct sway_seat *seat = e->seat->seat->data;\n\tif (e->serial == seat->last_button_serial) {\n\t\tseatop_begin_resize_floating(seat, view->container, e->edges);\n\t}\n}\n\nstatic void handle_unmap(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, unmap);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\n\tif (!sway_assert(view->surface, \"Cannot unmap unmapped view\")) {\n\t\treturn;\n\t}\n\n\tview_unmap(view);\n\n\twl_list_remove(&xdg_shell_view->new_popup.link);\n\twl_list_remove(&xdg_shell_view->request_maximize.link);\n\twl_list_remove(&xdg_shell_view->request_fullscreen.link);\n\twl_list_remove(&xdg_shell_view->request_move.link);\n\twl_list_remove(&xdg_shell_view->request_resize.link);\n\twl_list_remove(&xdg_shell_view->set_title.link);\n\twl_list_remove(&xdg_shell_view->set_app_id.link);\n}\n\nstatic void handle_map(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, map);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\tstruct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;\n\n\tview->natural_width = toplevel->base->geometry.width;\n\tview->natural_height = toplevel->base->geometry.height;\n\n\tbool csd = false;\n\n\tif (view->xdg_decoration) {\n\t\tenum wlr_xdg_toplevel_decoration_v1_mode mode =\n\t\t\tview->xdg_decoration->wlr_xdg_decoration->requested_mode;\n\t\tcsd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;\n\t} else {\n\t\tstruct sway_server_decoration *deco =\n\t\t\t\tdecoration_from_surface(toplevel->base->surface);\n\t\tcsd = !deco || deco->wlr_server_decoration->mode ==\n\t\t\tWLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;\n\t}\n\n\tview_map(view, toplevel->base->surface,\n\t\ttoplevel->requested.fullscreen,\n\t\ttoplevel->requested.fullscreen_output,\n\t\tcsd);\n\n\ttransaction_commit_dirty();\n\n\txdg_shell_view->new_popup.notify = handle_new_popup;\n\twl_signal_add(&toplevel->base->events.new_popup,\n\t\t&xdg_shell_view->new_popup);\n\n\txdg_shell_view->request_maximize.notify = handle_request_maximize;\n\twl_signal_add(&toplevel->events.request_maximize,\n\t\t\t&xdg_shell_view->request_maximize);\n\n\txdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;\n\twl_signal_add(&toplevel->events.request_fullscreen,\n\t\t\t&xdg_shell_view->request_fullscreen);\n\n\txdg_shell_view->request_move.notify = handle_request_move;\n\twl_signal_add(&toplevel->events.request_move,\n\t\t\t&xdg_shell_view->request_move);\n\n\txdg_shell_view->request_resize.notify = handle_request_resize;\n\twl_signal_add(&toplevel->events.request_resize,\n\t\t\t&xdg_shell_view->request_resize);\n\n\txdg_shell_view->set_title.notify = handle_set_title;\n\twl_signal_add(&toplevel->events.set_title,\n\t\t\t&xdg_shell_view->set_title);\n\n\txdg_shell_view->set_app_id.notify = handle_set_app_id;\n\twl_signal_add(&toplevel->events.set_app_id,\n\t\t\t&xdg_shell_view->set_app_id);\n}\n\nstatic void handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\twl_container_of(listener, xdg_shell_view, destroy);\n\tstruct sway_view *view = &xdg_shell_view->view;\n\tif (!sway_assert(view->surface == NULL, \"Tried to destroy a mapped view\")) {\n\t\treturn;\n\t}\n\twl_list_remove(&xdg_shell_view->destroy.link);\n\twl_list_remove(&xdg_shell_view->map.link);\n\twl_list_remove(&xdg_shell_view->unmap.link);\n\twl_list_remove(&xdg_shell_view->commit.link);\n\tview->wlr_xdg_toplevel = NULL;\n\tif (view->xdg_decoration) {\n\t\tview->xdg_decoration->view = NULL;\n\t}\n\tview_begin_destroy(view);\n}\n\nstruct sway_view *view_from_wlr_xdg_surface(\n\t\tstruct wlr_xdg_surface *xdg_surface) {\n\treturn xdg_surface->data;\n}\n\nvoid handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {\n\tstruct wlr_xdg_toplevel *xdg_toplevel = data;\n\n\tsway_log(SWAY_DEBUG, \"New xdg_shell toplevel title='%s' app_id='%s'\",\n\t\txdg_toplevel->title, xdg_toplevel->app_id);\n\twlr_xdg_surface_ping(xdg_toplevel->base);\n\n\tstruct sway_xdg_shell_view *xdg_shell_view =\n\t\tcalloc(1, sizeof(struct sway_xdg_shell_view));\n\tif (!sway_assert(xdg_shell_view, \"Failed to allocate view\")) {\n\t\treturn;\n\t}\n\n\tif (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) {\n\t\tfree(xdg_shell_view);\n\t\treturn;\n\t}\n\txdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;\n\n\txdg_shell_view->map.notify = handle_map;\n\twl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map);\n\n\txdg_shell_view->unmap.notify = handle_unmap;\n\twl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);\n\n\txdg_shell_view->commit.notify = handle_commit;\n\twl_signal_add(&xdg_toplevel->base->surface->events.commit,\n\t\t&xdg_shell_view->commit);\n\n\txdg_shell_view->destroy.notify = handle_destroy;\n\twl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy);\n\n\twlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);\n\txdg_shell_view->image_capture_tree =\n\t\twlr_scene_xdg_surface_create(&xdg_shell_view->view.image_capture_scene->tree, xdg_toplevel->base);\n\n\txdg_toplevel->base->data = xdg_shell_view;\n}\n\nvoid xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data) {\n\tconst struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event *event = data;\n\tstruct sway_view *view = view_from_wlr_xdg_surface(event->toplevel->base);\n\tstruct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);\n\tfree(xdg_shell_view->tag);\n\txdg_shell_view->tag = strdup(event->tag);\n\tview_execute_criteria(view);\n\ttransaction_commit_dirty();\n}\n"
  },
  {
    "path": "sway/desktop/xwayland.c",
    "content": "#include <float.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <wayland-server-core.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_xdg_activation_v1.h>\n#include <wlr/types/wlr_scene.h>\n#include <wlr/xwayland.h>\n#include <xcb/xcb_icccm.h>\n#include \"log.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/output.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n\nstatic const char *atom_map[ATOM_LAST] = {\n\t[NET_WM_WINDOW_TYPE_NORMAL] = \"_NET_WM_WINDOW_TYPE_NORMAL\",\n\t[NET_WM_WINDOW_TYPE_DIALOG] = \"_NET_WM_WINDOW_TYPE_DIALOG\",\n\t[NET_WM_WINDOW_TYPE_UTILITY] = \"_NET_WM_WINDOW_TYPE_UTILITY\",\n\t[NET_WM_WINDOW_TYPE_TOOLBAR] = \"_NET_WM_WINDOW_TYPE_TOOLBAR\",\n\t[NET_WM_WINDOW_TYPE_SPLASH] = \"_NET_WM_WINDOW_TYPE_SPLASH\",\n\t[NET_WM_WINDOW_TYPE_MENU] = \"_NET_WM_WINDOW_TYPE_MENU\",\n\t[NET_WM_WINDOW_TYPE_DROPDOWN_MENU] = \"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\",\n\t[NET_WM_WINDOW_TYPE_POPUP_MENU] = \"_NET_WM_WINDOW_TYPE_POPUP_MENU\",\n\t[NET_WM_WINDOW_TYPE_TOOLTIP] = \"_NET_WM_WINDOW_TYPE_TOOLTIP\",\n\t[NET_WM_WINDOW_TYPE_NOTIFICATION] = \"_NET_WM_WINDOW_TYPE_NOTIFICATION\",\n\t[NET_WM_STATE_MODAL] = \"_NET_WM_STATE_MODAL\",\n};\n\nstatic void unmanaged_handle_request_configure(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, request_configure);\n\tstruct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;\n\tstruct wlr_xwayland_surface_configure_event *ev = data;\n\twlr_xwayland_surface_configure(xsurface, ev->x, ev->y,\n\t\tev->width, ev->height);\n}\n\nstatic void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, set_geometry);\n\tstruct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;\n\n\twlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y);\n}\n\nstatic void unmanaged_handle_map(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, map);\n\tstruct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;\n\n\tsurface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged,\n\t\txsurface->surface);\n\n\tif (surface->surface_scene) {\n\t\tscene_descriptor_assign(&surface->surface_scene->buffer->node,\n\t\t\tSWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface);\n\t\twlr_scene_node_set_position(&surface->surface_scene->buffer->node,\n\t\t\txsurface->x, xsurface->y);\n\n\t\twl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);\n\t\tsurface->set_geometry.notify = unmanaged_handle_set_geometry;\n\t}\n\n\tif (wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) {\n\t\tstruct sway_seat *seat = input_manager_current_seat();\n\t\tstruct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;\n\t\twlr_xwayland_set_seat(xwayland, seat->wlr_seat);\n\t\tseat_set_focus_surface(seat, xsurface->surface, false);\n\t}\n}\n\nstatic void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, unmap);\n\tstruct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;\n\n\tif (surface->surface_scene) {\n\t\twl_list_remove(&surface->set_geometry.link);\n\n\t\twlr_scene_node_destroy(&surface->surface_scene->buffer->node);\n\t\tsurface->surface_scene = NULL;\n\t}\n\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tif (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {\n\t\t// This simply returns focus to the parent surface if there's one available.\n\t\t// This seems to handle JetBrains issues.\n\t\tif (xsurface->parent && xsurface->parent->surface\n\t\t\t\t&& wlr_xwayland_surface_override_redirect_wants_focus(xsurface->parent)) {\n\t\t\tseat_set_focus_surface(seat, xsurface->parent->surface, false);\n\t\t\treturn;\n\t\t}\n\n\t\t// Restore focus\n\t\tstruct sway_node *previous = seat_get_focus_inactive(seat, &root->node);\n\t\tif (previous) {\n\t\t\t// Hack to get seat to re-focus the return value of get_focus\n\t\t\tseat_set_focus(seat, NULL);\n\t\t\tseat_set_focus(seat, previous);\n\t\t}\n\t}\n}\n\nstatic void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, request_activate);\n\tstruct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_container *focus = seat_get_focused_container(seat);\n\tif (focus && focus->view && focus->view->pid != xsurface->pid) {\n\t\treturn;\n\t}\n\n\tseat_set_focus_surface(seat, xsurface->surface, false);\n}\n\nstatic void unmanaged_handle_associate(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, associate);\n\tstruct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;\n\twl_signal_add(&xsurface->surface->events.map, &surface->map);\n\tsurface->map.notify = unmanaged_handle_map;\n\twl_signal_add(&xsurface->surface->events.unmap, &surface->unmap);\n\tsurface->unmap.notify = unmanaged_handle_unmap;\n}\n\nstatic void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, dissociate);\n\twl_list_remove(&surface->map.link);\n\twl_list_remove(&surface->unmap.link);\n}\n\nstatic void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, destroy);\n\twl_list_remove(&surface->request_configure.link);\n\twl_list_remove(&surface->associate.link);\n\twl_list_remove(&surface->dissociate.link);\n\twl_list_remove(&surface->destroy.link);\n\twl_list_remove(&surface->override_redirect.link);\n\twl_list_remove(&surface->request_activate.link);\n\tfree(surface);\n}\n\nstatic void handle_map(struct wl_listener *listener, void *data);\nstatic void handle_associate(struct wl_listener *listener, void *data);\n\nstruct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface);\n\nstatic void unmanaged_handle_override_redirect(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\twl_container_of(listener, surface, override_redirect);\n\tstruct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;\n\n\tbool associated = xsurface->surface != NULL;\n\tbool mapped = associated && xsurface->surface->mapped;\n\tif (mapped) {\n\t\tunmanaged_handle_unmap(&surface->unmap, NULL);\n\t}\n\tif (associated) {\n\t\tunmanaged_handle_dissociate(&surface->dissociate, NULL);\n\t}\n\n\tunmanaged_handle_destroy(&surface->destroy, NULL);\n\txsurface->data = NULL;\n\n\tstruct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface);\n\tif (associated) {\n\t\thandle_associate(&xwayland_view->associate, NULL);\n\t}\n\tif (mapped) {\n\t\thandle_map(&xwayland_view->map, xsurface);\n\t}\n}\n\nstatic struct sway_xwayland_unmanaged *create_unmanaged(\n\t\tstruct wlr_xwayland_surface *xsurface) {\n\tstruct sway_xwayland_unmanaged *surface =\n\t\tcalloc(1, sizeof(struct sway_xwayland_unmanaged));\n\tif (surface == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Allocation failed\");\n\t\treturn NULL;\n\t}\n\n\tsurface->wlr_xwayland_surface = xsurface;\n\n\twl_signal_add(&xsurface->events.request_configure,\n\t\t&surface->request_configure);\n\tsurface->request_configure.notify = unmanaged_handle_request_configure;\n\twl_signal_add(&xsurface->events.associate, &surface->associate);\n\tsurface->associate.notify = unmanaged_handle_associate;\n\twl_signal_add(&xsurface->events.dissociate, &surface->dissociate);\n\tsurface->dissociate.notify = unmanaged_handle_dissociate;\n\twl_signal_add(&xsurface->events.destroy, &surface->destroy);\n\tsurface->destroy.notify = unmanaged_handle_destroy;\n\twl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);\n\tsurface->override_redirect.notify = unmanaged_handle_override_redirect;\n\twl_signal_add(&xsurface->events.request_activate, &surface->request_activate);\n\tsurface->request_activate.notify = unmanaged_handle_request_activate;\n\n\treturn surface;\n}\n\nstatic struct sway_xwayland_view *xwayland_view_from_view(\n\t\tstruct sway_view *view) {\n\tif (!sway_assert(view->type == SWAY_VIEW_XWAYLAND,\n\t\t\t\"Expected xwayland view\")) {\n\t\treturn NULL;\n\t}\n\treturn (struct sway_xwayland_view *)view;\n}\n\nstatic const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) {\n\tif (xwayland_view_from_view(view) == NULL) {\n\t\treturn NULL;\n\t}\n\tswitch (prop) {\n\tcase VIEW_PROP_TITLE:\n\t\treturn view->wlr_xwayland_surface->title;\n\tcase VIEW_PROP_CLASS:\n\t\treturn view->wlr_xwayland_surface->class;\n\tcase VIEW_PROP_INSTANCE:\n\t\treturn view->wlr_xwayland_surface->instance;\n\tcase VIEW_PROP_WINDOW_ROLE:\n\t\treturn view->wlr_xwayland_surface->role;\n\tdefault:\n\t\treturn NULL;\n\t}\n}\n\nstatic uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) {\n\tif (xwayland_view_from_view(view) == NULL) {\n\t\treturn 0;\n\t}\n\tswitch (prop) {\n\tcase VIEW_PROP_X11_WINDOW_ID:\n\t\treturn view->wlr_xwayland_surface->window_id;\n\tcase VIEW_PROP_X11_PARENT_ID:\n\t\tif (view->wlr_xwayland_surface->parent) {\n\t\t\treturn view->wlr_xwayland_surface->parent->window_id;\n\t\t}\n\t\treturn 0;\n\tcase VIEW_PROP_WINDOW_TYPE:\n\t\tif (view->wlr_xwayland_surface->window_type_len == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn view->wlr_xwayland_surface->window_type[0];\n\tdefault:\n\t\treturn 0;\n\t}\n}\n\nstatic uint32_t configure(struct sway_view *view, double lx, double ly, int width,\n\t\tint height) {\n\tstruct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);\n\tif (xwayland_view == NULL) {\n\t\treturn 0;\n\t}\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\n\twlr_xwayland_surface_configure(xsurface, lx, ly, width, height);\n\n\t// xwayland doesn't give us a serial for the configure\n\treturn 0;\n}\n\nstatic void set_activated(struct sway_view *view, bool activated) {\n\tif (xwayland_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\tstruct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;\n\n\tif (activated && surface->minimized) {\n\t\twlr_xwayland_surface_set_minimized(surface, false);\n\t}\n\n\twlr_xwayland_surface_activate(surface, activated);\n}\n\nstatic void set_tiled(struct sway_view *view, bool tiled) {\n\tif (xwayland_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\tstruct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;\n\twlr_xwayland_surface_set_maximized(surface, tiled, tiled);\n}\n\nstatic void set_fullscreen(struct sway_view *view, bool fullscreen) {\n\tif (xwayland_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\tstruct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;\n\twlr_xwayland_surface_set_fullscreen(surface, fullscreen);\n}\n\nstatic bool wants_floating(struct sway_view *view) {\n\tif (xwayland_view_from_view(view) == NULL) {\n\t\treturn false;\n\t}\n\tstruct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;\n\tstruct sway_xwayland *xwayland = &server.xwayland;\n\n\tif (surface->modal) {\n\t\treturn true;\n\t}\n\n\tfor (size_t i = 0; i < surface->window_type_len; ++i) {\n\t\txcb_atom_t type = surface->window_type[i];\n\t\tif (type == xwayland->atoms[NET_WM_WINDOW_TYPE_DIALOG] ||\n\t\t\t\ttype == xwayland->atoms[NET_WM_WINDOW_TYPE_UTILITY] ||\n\t\t\t\ttype == xwayland->atoms[NET_WM_WINDOW_TYPE_TOOLBAR] ||\n\t\t\t\ttype == xwayland->atoms[NET_WM_WINDOW_TYPE_SPLASH]) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\txcb_size_hints_t *size_hints = surface->size_hints;\n\tif (size_hints != NULL &&\n\t\t\tsize_hints->min_width > 0 && size_hints->min_height > 0 &&\n\t\t\t(size_hints->max_width == size_hints->min_width ||\n\t\t\tsize_hints->max_height == size_hints->min_height)) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic void handle_set_decorations(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, set_decorations);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\n\tbool csd = xsurface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL;\n\tview_update_csd_from_client(view, csd);\n}\n\nstatic bool is_transient_for(struct sway_view *child,\n\t\tstruct sway_view *ancestor) {\n\tif (xwayland_view_from_view(child) == NULL) {\n\t\treturn false;\n\t}\n\tstruct wlr_xwayland_surface *surface = child->wlr_xwayland_surface;\n\twhile (surface) {\n\t\tif (surface->parent == ancestor->wlr_xwayland_surface) {\n\t\t\treturn true;\n\t\t}\n\t\tsurface = surface->parent;\n\t}\n\treturn false;\n}\n\nstatic void _close(struct sway_view *view) {\n\tif (xwayland_view_from_view(view) == NULL) {\n\t\treturn;\n\t}\n\twlr_xwayland_surface_close(view->wlr_xwayland_surface);\n}\n\nstatic void destroy(struct sway_view *view) {\n\tstruct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);\n\tif (xwayland_view == NULL) {\n\t\treturn;\n\t}\n\tfree(xwayland_view);\n}\n\nstatic void get_constraints(struct sway_view *view, double *min_width,\n\t\tdouble *max_width, double *min_height, double *max_height) {\n\tstruct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;\n\txcb_size_hints_t *size_hints = surface->size_hints;\n\n\tif (size_hints == NULL) {\n\t\t*min_width = DBL_MIN;\n\t\t*max_width = DBL_MAX;\n\t\t*min_height = DBL_MIN;\n\t\t*max_height = DBL_MAX;\n\t\treturn;\n\t}\n\n\t*min_width = size_hints->min_width > 0 ? size_hints->min_width : DBL_MIN;\n\t*max_width = size_hints->max_width > 0 ? size_hints->max_width : DBL_MAX;\n\t*min_height = size_hints->min_height > 0 ? size_hints->min_height : DBL_MIN;\n\t*max_height = size_hints->max_height > 0 ? size_hints->max_height : DBL_MAX;\n}\n\nstatic const struct sway_view_impl view_impl = {\n\t.get_constraints = get_constraints,\n\t.get_string_prop = get_string_prop,\n\t.get_int_prop = get_int_prop,\n\t.configure = configure,\n\t.set_activated = set_activated,\n\t.set_tiled = set_tiled,\n\t.set_fullscreen = set_fullscreen,\n\t.wants_floating = wants_floating,\n\t.is_transient_for = is_transient_for,\n\t.close = _close,\n\t.destroy = destroy,\n};\n\nstatic void handle_commit(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, commit);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tstruct wlr_surface_state *state = &xsurface->surface->current;\n\n\tstruct wlr_box new_geo = {0};\n\tnew_geo.width = state->width;\n\tnew_geo.height = state->height;\n\n\tbool new_size = new_geo.width != view->geometry.width ||\n\t\t\tnew_geo.height != view->geometry.height;\n\n\tif (new_size) {\n\t\t// The client changed its surface size in this commit. For floating\n\t\t// containers, we resize the container to match. For tiling containers,\n\t\t// we only recenter the surface.\n\t\tmemcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));\n\t\tif (container_is_floating(view->container)) {\n\t\t\tview_update_size(view);\n\t\t\ttransaction_commit_dirty_client();\n\t\t}\n\n\t\tview_center_and_clip_surface(view);\n\t}\n\n\tif (view->container->node.instruction) {\n\t\tbool successful = transaction_notify_view_ready_by_geometry(view,\n\t\t\t\txsurface->x, xsurface->y, state->width, state->height);\n\n\t\t// If we saved the view and this commit isn't what we're looking for\n\t\t// that means the user will never actually see the buffers submitted to\n\t\t// us here. Just send frame done events to these surfaces so they can\n\t\t// commit another time for us.\n\t\tif (view->saved_surface_tree && !successful) {\n\t\t\tview_send_frame_done(view);\n\t\t}\n\t}\n}\n\nstatic void handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, destroy);\n\tstruct sway_view *view = &xwayland_view->view;\n\n\tif (view->surface) {\n\t\tview_unmap(view);\n\t\twl_list_remove(&xwayland_view->commit.link);\n\t}\n\n\txwayland_view->view.wlr_xwayland_surface = NULL;\n\n\twl_list_remove(&xwayland_view->destroy.link);\n\twl_list_remove(&xwayland_view->request_configure.link);\n\twl_list_remove(&xwayland_view->request_fullscreen.link);\n\twl_list_remove(&xwayland_view->request_minimize.link);\n\twl_list_remove(&xwayland_view->request_move.link);\n\twl_list_remove(&xwayland_view->request_resize.link);\n\twl_list_remove(&xwayland_view->request_activate.link);\n\twl_list_remove(&xwayland_view->set_title.link);\n\twl_list_remove(&xwayland_view->set_class.link);\n\twl_list_remove(&xwayland_view->set_role.link);\n\twl_list_remove(&xwayland_view->set_startup_id.link);\n\twl_list_remove(&xwayland_view->set_window_type.link);\n\twl_list_remove(&xwayland_view->set_hints.link);\n\twl_list_remove(&xwayland_view->set_decorations.link);\n\twl_list_remove(&xwayland_view->associate.link);\n\twl_list_remove(&xwayland_view->dissociate.link);\n\twl_list_remove(&xwayland_view->override_redirect.link);\n\tview_begin_destroy(&xwayland_view->view);\n}\n\nstatic void handle_unmap(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, unmap);\n\tstruct sway_view *view = &xwayland_view->view;\n\n\tif (!sway_assert(view->surface, \"Cannot unmap unmapped view\")) {\n\t\treturn;\n\t}\n\n\twl_list_remove(&xwayland_view->commit.link);\n\twl_list_remove(&xwayland_view->surface_tree_destroy.link);\n\n\twlr_scene_node_destroy(&xwayland_view->image_capture_scene_surface->buffer->node);\n\txwayland_view->image_capture_scene_surface = NULL;\n\n\tif (xwayland_view->surface_tree) {\n\t\twlr_scene_node_destroy(&xwayland_view->surface_tree->node);\n\t\txwayland_view->surface_tree = NULL;\n\t}\n\n\tview_unmap(view);\n}\n\nstatic void handle_surface_tree_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view,\n\t\tsurface_tree_destroy);\n\txwayland_view->surface_tree = NULL;\n}\n\nstatic void handle_map(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, map);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\n\tview->natural_width = xsurface->width;\n\tview->natural_height = xsurface->height;\n\n\t// Wire up the commit listener here, because xwayland map/unmap can change\n\t// the underlying wlr_surface\n\twl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit);\n\txwayland_view->commit.notify = handle_commit;\n\n\t// Put it back into the tree\n\tview_map(view, xsurface->surface, xsurface->fullscreen, NULL, false);\n\n\txwayland_view->surface_tree = wlr_scene_subsurface_tree_create(\n\t\txwayland_view->view.content_tree, xsurface->surface);\n\n\tif (xwayland_view->surface_tree) {\n\t\txwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy;\n\t\twl_signal_add(&xwayland_view->surface_tree->node.events.destroy,\n\t\t\t&xwayland_view->surface_tree_destroy);\n\t}\n\n\txwayland_view->image_capture_scene_surface =\n\t\twlr_scene_surface_create(&xwayland_view->view.image_capture_scene->tree, xsurface->surface);\n\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_dissociate(struct wl_listener *listener, void *data);\n\nstatic void handle_override_redirect(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, override_redirect);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\n\tbool associated = xsurface->surface != NULL;\n\tbool mapped = associated && xsurface->surface->mapped;\n\tif (mapped) {\n\t\thandle_unmap(&xwayland_view->unmap, NULL);\n\t}\n\tif (associated) {\n\t\thandle_dissociate(&xwayland_view->dissociate, NULL);\n\t}\n\n\thandle_destroy(&xwayland_view->destroy, view);\n\txsurface->data = NULL;\n\n\tstruct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface);\n\tif (associated) {\n\t\tunmanaged_handle_associate(&unmanaged->associate, NULL);\n\t}\n\tif (mapped) {\n\t\tunmanaged_handle_map(&unmanaged->map, xsurface);\n\t}\n}\n\nstatic void handle_request_configure(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, request_configure);\n\tstruct wlr_xwayland_surface_configure_event *ev = data;\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\twlr_xwayland_surface_configure(xsurface, ev->x, ev->y,\n\t\t\tev->width, ev->height);\n\t\treturn;\n\t}\n\tif (container_is_floating(view->container)) {\n\t\t// Respect minimum and maximum sizes\n\t\tview->natural_width = ev->width;\n\t\tview->natural_height = ev->height;\n\t\tcontainer_floating_resize_and_center(view->container);\n\n\t\tconfigure(view, view->container->pending.content_x,\n\t\t\t\tview->container->pending.content_y,\n\t\t\t\tview->container->pending.content_width,\n\t\t\t\tview->container->pending.content_height);\n\t\tnode_set_dirty(&view->container->node);\n\t} else {\n\t\tconfigure(view, view->container->current.content_x,\n\t\t\t\tview->container->current.content_y,\n\t\t\t\tview->container->current.content_width,\n\t\t\t\tview->container->current.content_height);\n\t}\n}\n\nstatic void handle_request_fullscreen(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, request_fullscreen);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tcontainer_set_fullscreen(view->container, xsurface->fullscreen);\n\n\tarrange_root();\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_request_minimize(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, request_minimize);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\n\tstruct wlr_xwayland_minimize_event *e = data;\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tbool focused = seat_get_focus(seat) == &view->container->node;\n\twlr_xwayland_surface_set_minimized(xsurface, !focused && e->minimize);\n}\n\nstatic void handle_request_move(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, request_move);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tif (!container_is_floating(view->container) ||\n\t\t\tview->container->pending.fullscreen_mode) {\n\t\treturn;\n\t}\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tseatop_begin_move_floating(seat, view->container);\n}\n\nstatic void handle_request_resize(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, request_resize);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tif (!container_is_floating(view->container)) {\n\t\treturn;\n\t}\n\tstruct wlr_xwayland_resize_event *e = data;\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tseatop_begin_resize_floating(seat, view->container, e->edges);\n}\n\nstatic void handle_request_activate(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, request_activate);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tview_request_activate(view, NULL);\n\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_set_title(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, set_title);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tview_update_title(view, false);\n\tview_execute_criteria(view);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_set_class(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, set_class);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tview_execute_criteria(view);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_set_role(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, set_role);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tview_execute_criteria(view);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_set_startup_id(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, set_startup_id);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->startup_id == NULL) {\n\t\treturn;\n\t}\n\n\tstruct wlr_xdg_activation_token_v1 *token =\n\t\twlr_xdg_activation_v1_find_token(\n\t\t\t\tserver.xdg_activation_v1, xsurface->startup_id);\n\tif (token == NULL) {\n\t\t// Tried to activate with an unknown or expired token\n\t\treturn;\n\t}\n\n\tstruct launcher_ctx *ctx = token->data;\n\tif (token->data == NULL) {\n\t\t// TODO: support external launchers in X\n\t\treturn;\n\t}\n\tview_assign_ctx(view, ctx);\n}\n\nstatic void handle_set_window_type(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, set_window_type);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tview_execute_criteria(view);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_set_hints(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, set_hints);\n\tstruct sway_view *view = &xwayland_view->view;\n\tstruct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;\n\tif (xsurface->surface == NULL || !xsurface->surface->mapped) {\n\t\treturn;\n\t}\n\tconst bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);\n\tif (!hints_urgency && view->urgent_timer) {\n\t\t// The view is in the timeout period. We'll ignore the request to\n\t\t// unset urgency so that the view remains urgent until the timer clears\n\t\t// it.\n\t\treturn;\n\t}\n\tif (view->allow_request_urgent) {\n\t\tview_set_urgent(view, hints_urgency);\n\t}\n}\n\nstatic void handle_associate(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, associate);\n\tstruct wlr_xwayland_surface *xsurface =\n\t\txwayland_view->view.wlr_xwayland_surface;\n\twl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap);\n\txwayland_view->unmap.notify = handle_unmap;\n\twl_signal_add(&xsurface->surface->events.map, &xwayland_view->map);\n\txwayland_view->map.notify = handle_map;\n}\n\nstatic void handle_dissociate(struct wl_listener *listener, void *data) {\n\tstruct sway_xwayland_view *xwayland_view =\n\t\twl_container_of(listener, xwayland_view, dissociate);\n\twl_list_remove(&xwayland_view->map.link);\n\twl_list_remove(&xwayland_view->unmap.link);\n}\n\nstruct sway_view *view_from_wlr_xwayland_surface(\n\t\tstruct wlr_xwayland_surface *xsurface) {\n\treturn xsurface->data;\n}\n\nstruct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface) {\n\tsway_log(SWAY_DEBUG, \"New xwayland surface title='%s' class='%s'\",\n\t\txsurface->title, xsurface->class);\n\n\tstruct sway_xwayland_view *xwayland_view =\n\t\tcalloc(1, sizeof(struct sway_xwayland_view));\n\tif (!sway_assert(xwayland_view, \"Failed to allocate view\")) {\n\t\treturn NULL;\n\t}\n\n\tif (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) {\n\t\tfree(xwayland_view);\n\t\treturn NULL;\n\t}\n\txwayland_view->view.wlr_xwayland_surface = xsurface;\n\n\twl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);\n\txwayland_view->destroy.notify = handle_destroy;\n\n\twl_signal_add(&xsurface->events.request_configure,\n\t\t&xwayland_view->request_configure);\n\txwayland_view->request_configure.notify = handle_request_configure;\n\n\twl_signal_add(&xsurface->events.request_fullscreen,\n\t\t&xwayland_view->request_fullscreen);\n\txwayland_view->request_fullscreen.notify = handle_request_fullscreen;\n\n\twl_signal_add(&xsurface->events.request_minimize,\n\t\t&xwayland_view->request_minimize);\n\txwayland_view->request_minimize.notify = handle_request_minimize;\n\n\twl_signal_add(&xsurface->events.request_activate,\n\t\t&xwayland_view->request_activate);\n\txwayland_view->request_activate.notify = handle_request_activate;\n\n\twl_signal_add(&xsurface->events.request_move,\n\t\t&xwayland_view->request_move);\n\txwayland_view->request_move.notify = handle_request_move;\n\n\twl_signal_add(&xsurface->events.request_resize,\n\t\t&xwayland_view->request_resize);\n\txwayland_view->request_resize.notify = handle_request_resize;\n\n\twl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title);\n\txwayland_view->set_title.notify = handle_set_title;\n\n\twl_signal_add(&xsurface->events.set_class, &xwayland_view->set_class);\n\txwayland_view->set_class.notify = handle_set_class;\n\n\twl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);\n\txwayland_view->set_role.notify = handle_set_role;\n\n\twl_signal_add(&xsurface->events.set_startup_id,\n\t\t\t&xwayland_view->set_startup_id);\n\txwayland_view->set_startup_id.notify = handle_set_startup_id;\n\n\twl_signal_add(&xsurface->events.set_window_type,\n\t\t\t&xwayland_view->set_window_type);\n\txwayland_view->set_window_type.notify = handle_set_window_type;\n\n\twl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints);\n\txwayland_view->set_hints.notify = handle_set_hints;\n\n\twl_signal_add(&xsurface->events.set_decorations,\n\t\t\t&xwayland_view->set_decorations);\n\txwayland_view->set_decorations.notify = handle_set_decorations;\n\n\twl_signal_add(&xsurface->events.associate, &xwayland_view->associate);\n\txwayland_view->associate.notify = handle_associate;\n\n\twl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate);\n\txwayland_view->dissociate.notify = handle_dissociate;\n\n\twl_signal_add(&xsurface->events.set_override_redirect,\n\t\t\t&xwayland_view->override_redirect);\n\txwayland_view->override_redirect.notify = handle_override_redirect;\n\n\txsurface->data = xwayland_view;\n\n\treturn xwayland_view;\n}\n\nvoid handle_xwayland_surface(struct wl_listener *listener, void *data) {\n\tstruct wlr_xwayland_surface *xsurface = data;\n\n\tif (xsurface->override_redirect) {\n\t\tsway_log(SWAY_DEBUG, \"New xwayland unmanaged surface\");\n\t\tcreate_unmanaged(xsurface);\n\t\treturn;\n\t}\n\n\tcreate_xwayland_view(xsurface);\n}\n\nvoid handle_xwayland_ready(struct wl_listener *listener, void *data) {\n\tstruct sway_server *server =\n\t\twl_container_of(listener, server, xwayland_ready);\n\tstruct sway_xwayland *xwayland = &server->xwayland;\n\n\txcb_connection_t *xcb_conn = xcb_connect(NULL, NULL);\n\tint err = xcb_connection_has_error(xcb_conn);\n\tif (err) {\n\t\tsway_log(SWAY_ERROR, \"XCB connect failed: %d\", err);\n\t\treturn;\n\t}\n\n\txcb_intern_atom_cookie_t cookies[ATOM_LAST];\n\tfor (size_t i = 0; i < ATOM_LAST; i++) {\n\t\tcookies[i] =\n\t\t\txcb_intern_atom(xcb_conn, 0, strlen(atom_map[i]), atom_map[i]);\n\t}\n\tfor (size_t i = 0; i < ATOM_LAST; i++) {\n\t\txcb_generic_error_t *error = NULL;\n\t\txcb_intern_atom_reply_t *reply =\n\t\t\txcb_intern_atom_reply(xcb_conn, cookies[i], &error);\n\t\tif (reply != NULL && error == NULL) {\n\t\t\txwayland->atoms[i] = reply->atom;\n\t\t}\n\t\tfree(reply);\n\n\t\tif (error != NULL) {\n\t\t\tsway_log(SWAY_ERROR, \"could not resolve atom %s, X11 error code %d\",\n\t\t\t\tatom_map[i], error->error_code);\n\t\t\tfree(error);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\txcb_disconnect(xcb_conn);\n}\n"
  },
  {
    "path": "sway/input/cursor.c",
    "content": "#include <assert.h>\n#include <math.h>\n#include <libevdev/libevdev.h>\n#include <linux/input-event-codes.h>\n#include <errno.h>\n#include <time.h>\n#include <strings.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_cursor_shape_v1.h>\n#include <wlr/types/wlr_pointer.h>\n#include <wlr/types/wlr_relative_pointer_v1.h>\n#include <wlr/types/wlr_touch.h>\n#include <wlr/types/wlr_tablet_v2.h>\n#include <wlr/types/wlr_tablet_pad.h>\n#include <wlr/types/wlr_tablet_tool.h>\n#include <wlr/types/wlr_xcursor_manager.h>\n#include <wlr/util/region.h>\n#include \"config.h\"\n#include \"log.h\"\n#include \"util.h\"\n#include \"sway/commands.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/input/tablet.h\"\n#include \"sway/layers.h\"\n#include \"sway/output.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"wlr-layer-shell-unstable-v1-protocol.h\"\n\n/**\n * Returns the node at the cursor's position. If there is a surface at that\n * location, it is stored in **surface (it may not be a view).\n */\nstruct sway_node *node_at_coords(\n\t\tstruct sway_seat *seat, double lx, double ly,\n\t\tstruct wlr_surface **surface, double *sx, double *sy) {\n\tstruct wlr_scene_node *scene_node = NULL;\n\n\tstruct wlr_scene_node *node;\n\twl_list_for_each_reverse(node, &root->layer_tree->children, link) {\n\t\tstruct wlr_scene_tree *layer = wlr_scene_tree_from_node(node);\n\n\t\tbool non_interactive = scene_descriptor_try_get(&layer->node,\n\t\t\tSWAY_SCENE_DESC_NON_INTERACTIVE);\n\t\tif (non_interactive) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tscene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy);\n\t\tif (scene_node) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (scene_node) {\n\t\t// determine what wlr_surface we clicked on\n\t\tif (scene_node->type == WLR_SCENE_NODE_BUFFER) {\n\t\t\tstruct wlr_scene_buffer *scene_buffer =\n\t\t\t\twlr_scene_buffer_from_node(scene_node);\n\t\t\tstruct wlr_scene_surface *scene_surface =\n\t\t\t\twlr_scene_surface_try_from_buffer(scene_buffer);\n\n\t\t\tif (scene_surface) {\n\t\t\t\t*surface = scene_surface->surface;\n\t\t\t}\n\t\t}\n\n\t\t// determine what container we clicked on\n\t\tstruct wlr_scene_node *current = scene_node;\n\t\twhile (true) {\n\t\t\tstruct sway_container *con = scene_descriptor_try_get(current,\n\t\t\t\tSWAY_SCENE_DESC_CONTAINER);\n\n\t\t\tif (!con) {\n\t\t\t\tstruct sway_view *view = scene_descriptor_try_get(current,\n\t\t\t\t\tSWAY_SCENE_DESC_VIEW);\n\t\t\t\tif (view) {\n\t\t\t\t\tcon = view->container;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!con) {\n\t\t\t\tstruct sway_popup_desc *popup =\n\t\t\t\t\tscene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);\n\t\t\t\tif (popup && popup->view) {\n\t\t\t\t\tcon = popup->view->container;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (con && (!con->view || con->view->surface)) {\n\t\t\t\treturn &con->node;\n\t\t\t}\n\n\t\t\tif (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) {\n\t\t\t\t// We don't want to feed through the current workspace on\n\t\t\t\t// layer shells\n\t\t\t\treturn NULL;\n\t\t\t}\n\n#if WLR_HAS_XWAYLAND\n\t\t\tif (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) {\n\t\t\t\treturn NULL;\n\t\t\t}\n#endif\n\n\t\t\tif (!current->parent) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcurrent = &current->parent->node;\n\t\t}\n\t}\n\n\t// if we aren't on a container, determine what workspace we are on\n\tstruct wlr_output *wlr_output = wlr_output_layout_output_at(\n\t\t\troot->output_layout, lx, ly);\n\tif (wlr_output == NULL) {\n\t\treturn NULL;\n\t}\n\n\tstruct sway_output *output = wlr_output->data;\n\tif (!output || !output->enabled) {\n\t\t// output is being destroyed or is being enabled\n\t\treturn NULL;\n\t}\n\n\tstruct sway_workspace *ws = output_get_active_workspace(output);\n\tif (!ws) {\n\t\treturn NULL;\n\t}\n\n\treturn &ws->node;\n}\n\nvoid cursor_rebase(struct sway_cursor *cursor) {\n\tuint32_t time_msec = get_current_time_in_msec();\n\tseatop_rebase(cursor->seat, time_msec);\n}\n\nvoid cursor_rebase_all(void) {\n\tif (!root->outputs->length) {\n\t\treturn;\n\t}\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tcursor_rebase(seat->cursor);\n\t}\n}\n\nvoid cursor_update_image(struct sway_cursor *cursor,\n\t\tstruct sway_node *node) {\n\tif (node && node->type == N_CONTAINER) {\n\t\t// Try a node's resize edge\n\t\tenum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor);\n\t\tif (edge == WLR_EDGE_NONE) {\n\t\t\tcursor_set_image(cursor, \"default\", NULL);\n\t\t} else if (container_is_floating(node->sway_container)) {\n\t\t\tcursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);\n\t\t} else {\n\t\t\tif (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) {\n\t\t\t\tcursor_set_image(cursor, \"col-resize\", NULL);\n\t\t\t} else {\n\t\t\t\tcursor_set_image(cursor, \"row-resize\", NULL);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tcursor_set_image(cursor, \"default\", NULL);\n\t}\n}\n\nstatic void cursor_hide(struct sway_cursor *cursor) {\n\twlr_cursor_unset_image(cursor->cursor);\n\tcursor->hidden = true;\n\twlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat);\n}\n\nstatic int hide_notify(void *data) {\n\tstruct sway_cursor *cursor = data;\n\tcursor_hide(cursor);\n\treturn 1;\n}\n\nint cursor_get_timeout(struct sway_cursor *cursor) {\n\tif (cursor->pressed_button_count > 0) {\n\t\t// Do not hide cursor unless all buttons are released\n\t\treturn 0;\n\t}\n\n\tstruct seat_config *sc = seat_get_config(cursor->seat);\n\tif (!sc) {\n\t\tsc = seat_get_config_by_name(\"*\");\n\t}\n\tint timeout = sc ? sc->hide_cursor_timeout : 0;\n\tif (timeout < 0) {\n\t\ttimeout = 0;\n\t}\n\treturn timeout;\n}\n\nvoid cursor_notify_key_press(struct sway_cursor *cursor) {\n\tif (cursor->hidden) {\n\t\treturn;\n\t}\n\n\tif (cursor->hide_when_typing == HIDE_WHEN_TYPING_DEFAULT) {\n\t\t// No cached value, need to lookup in the seat_config\n\t\tconst struct seat_config *seat_config = seat_get_config(cursor->seat);\n\t\tif (!seat_config) {\n\t\t\tseat_config = seat_get_config_by_name(\"*\");\n\t\t\tif (!seat_config) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tcursor->hide_when_typing = seat_config->hide_cursor_when_typing;\n\t\t// The default is currently disabled\n\t\tif (cursor->hide_when_typing == HIDE_WHEN_TYPING_DEFAULT) {\n\t\t\tcursor->hide_when_typing = HIDE_WHEN_TYPING_DISABLE;\n\t\t}\n\t}\n\n\tif (cursor->hide_when_typing == HIDE_WHEN_TYPING_ENABLE) {\n\t\tcursor_hide(cursor);\n\t}\n}\n\nstatic enum sway_input_idle_source idle_source_from_device(\n\t\tstruct wlr_input_device *device) {\n\tswitch (device->type) {\n\tcase WLR_INPUT_DEVICE_KEYBOARD:\n\t\treturn IDLE_SOURCE_KEYBOARD;\n\tcase WLR_INPUT_DEVICE_POINTER:\n\t\treturn IDLE_SOURCE_POINTER;\n\tcase WLR_INPUT_DEVICE_TOUCH:\n\t\treturn IDLE_SOURCE_TOUCH;\n\tcase WLR_INPUT_DEVICE_TABLET:\n\t\treturn IDLE_SOURCE_TABLET_TOOL;\n\tcase WLR_INPUT_DEVICE_TABLET_PAD:\n\t\treturn IDLE_SOURCE_TABLET_PAD;\n\tcase WLR_INPUT_DEVICE_SWITCH:\n\t\treturn IDLE_SOURCE_SWITCH;\n\t}\n\n\tabort();\n}\n\nvoid cursor_handle_activity_from_idle_source(struct sway_cursor *cursor,\n\t\tenum sway_input_idle_source idle_source) {\n\twl_event_source_timer_update(\n\t\t\tcursor->hide_source, cursor_get_timeout(cursor));\n\n\tseat_idle_notify_activity(cursor->seat, idle_source);\n\tif (idle_source != IDLE_SOURCE_TOUCH) {\n\t\tcursor_unhide(cursor);\n\t}\n}\n\nvoid cursor_handle_activity_from_device(struct sway_cursor *cursor,\n\t\tstruct wlr_input_device *device) {\n\tenum sway_input_idle_source idle_source = idle_source_from_device(device);\n\tcursor_handle_activity_from_idle_source(cursor, idle_source);\n}\n\nvoid cursor_unhide(struct sway_cursor *cursor) {\n\tif (!cursor->hidden) {\n\t\treturn;\n\t}\n\n\tcursor->hidden = false;\n\tif (cursor->image_surface) {\n\t\tcursor_set_image_surface(cursor,\n\t\t\t\tcursor->image_surface,\n\t\t\t\tcursor->hotspot_x,\n\t\t\t\tcursor->hotspot_y,\n\t\t\t\tcursor->image_client);\n\t} else {\n\t\tconst char *image = cursor->image;\n\t\tcursor->image = NULL;\n\t\tcursor_set_image(cursor, image, cursor->image_client);\n\t}\n\tcursor_rebase(cursor);\n\twl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor));\n}\n\nvoid pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, double dx, double dy,\n\t\tdouble dx_unaccel, double dy_unaccel) {\n\twlr_relative_pointer_manager_v1_send_relative_motion(\n\t\tserver.relative_pointer_manager,\n\t\tcursor->seat->wlr_seat, (uint64_t)time_msec * 1000,\n\t\tdx, dy, dx_unaccel, dy_unaccel);\n\n\t// Only apply pointer constraints to real pointer input.\n\tif (cursor->active_constraint && device->type == WLR_INPUT_DEVICE_POINTER) {\n\t\tstruct wlr_surface *surface = NULL;\n\t\tdouble sx, sy;\n\t\tnode_at_coords(cursor->seat,\n\t\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\t\tif (cursor->active_constraint->surface != surface) {\n\t\t\treturn;\n\t\t}\n\n\t\tdouble sx_confined, sy_confined;\n\t\tif (!wlr_region_confine(&cursor->confine, sx, sy, sx + dx, sy + dy,\n\t\t\t\t&sx_confined, &sy_confined)) {\n\t\t\treturn;\n\t\t}\n\n\t\tdx = sx_confined - sx;\n\t\tdy = sy_confined - sy;\n\t}\n\n\twlr_cursor_move(cursor->cursor, device, dx, dy);\n\n\tseatop_pointer_motion(cursor->seat, time_msec);\n}\n\nstatic void handle_pointer_motion_relative(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, motion);\n\tstruct wlr_pointer_motion_event *e = data;\n\tcursor_handle_activity_from_device(cursor, &e->pointer->base);\n\n\tpointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x,\n\t\te->delta_y, e->unaccel_dx, e->unaccel_dy);\n}\n\nstatic void handle_pointer_motion_absolute(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor =\n\t\twl_container_of(listener, cursor, motion_absolute);\n\tstruct wlr_pointer_motion_absolute_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\n\tdouble lx, ly;\n\twlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base,\n\t\t\tevent->x, event->y, &lx, &ly);\n\n\tdouble dx = lx - cursor->cursor->x;\n\tdouble dy = ly - cursor->cursor->y;\n\n\tpointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy,\n\t\tdx, dy);\n}\n\nvoid dispatch_cursor_button(struct sway_cursor *cursor,\n\t\tstruct wlr_input_device *device, uint32_t time_msec, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tif (time_msec == 0) {\n\t\ttime_msec = get_current_time_in_msec();\n\t}\n\n\tseatop_button(cursor->seat, time_msec, device, button, state);\n}\n\nstatic void handle_pointer_button(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, button);\n\tstruct wlr_pointer_button_event *event = data;\n\n\tif (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\tcursor->pressed_button_count++;\n\t} else {\n\t\tif (cursor->pressed_button_count > 0) {\n\t\t\tcursor->pressed_button_count--;\n\t\t} else {\n\t\t\tsway_log(SWAY_ERROR, \"Pressed button count was wrong\");\n\t\t}\n\t}\n\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tdispatch_cursor_button(cursor, &event->pointer->base,\n\t\t\tevent->time_msec, event->button, event->state);\n}\n\nvoid dispatch_cursor_axis(struct sway_cursor *cursor,\n\t\tstruct wlr_pointer_axis_event *event) {\n\tseatop_pointer_axis(cursor->seat, event);\n}\n\nstatic void handle_pointer_axis(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, axis);\n\tstruct wlr_pointer_axis_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tdispatch_cursor_axis(cursor, event);\n}\n\nstatic void handle_pointer_frame(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, frame);\n\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n}\n\nstatic void handle_touch_down(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);\n\tstruct wlr_touch_down_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->touch->base);\n\tcursor_hide(cursor);\n\n\tstruct sway_seat *seat = cursor->seat;\n\n\tdouble lx, ly;\n\twlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,\n\t\t\tevent->x, event->y, &lx, &ly);\n\n\tseat->touch_id = event->touch_id;\n\tseat->touch_x = lx;\n\tseat->touch_y = ly;\n\n\tseatop_touch_down(seat, event, lx, ly);\n}\n\nstatic void handle_touch_up(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);\n\tstruct wlr_touch_up_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->touch->base);\n\n\tstruct sway_seat *seat = cursor->seat;\n\n\tif (cursor->simulating_pointer_from_touch) {\n\t\tif (cursor->pointer_touch_id == cursor->seat->touch_id) {\n\t\t\tcursor->pointer_touch_up = true;\n\t\t\tdispatch_cursor_button(cursor, &event->touch->base,\n\t\t\t\tevent->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);\n\t\t}\n\t} else {\n\t\tseatop_touch_up(seat, event);\n\t}\n}\n\nstatic void handle_touch_cancel(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel);\n\tstruct wlr_touch_cancel_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->touch->base);\n\n\tstruct sway_seat *seat = cursor->seat;\n\n\tif (cursor->simulating_pointer_from_touch) {\n\t\tif (cursor->pointer_touch_id == cursor->seat->touch_id) {\n\t\t\tcursor->pointer_touch_up = true;\n\t\t\tdispatch_cursor_button(cursor, &event->touch->base,\n\t\t\t\tevent->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);\n\t\t}\n\t} else {\n\t\tseatop_touch_cancel(seat, event);\n\t}\n}\n\nstatic void handle_touch_motion(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor =\n\t\twl_container_of(listener, cursor, touch_motion);\n\tstruct wlr_touch_motion_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->touch->base);\n\n\tstruct sway_seat *seat = cursor->seat;\n\n\tdouble lx, ly;\n\twlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,\n\t\t\tevent->x, event->y, &lx, &ly);\n\n\tif (seat->touch_id == event->touch_id) {\n\t\tseat->touch_x = lx;\n\t\tseat->touch_y = ly;\n\n\t\tdrag_icons_update_position(seat);\n\t}\n\n\tif (cursor->simulating_pointer_from_touch) {\n\t\tif (seat->touch_id == cursor->pointer_touch_id) {\n\t\t\tdouble dx, dy;\n\t\t\tdx = lx - cursor->cursor->x;\n\t\t\tdy = ly - cursor->cursor->y;\n\t\t\tpointer_motion(cursor, event->time_msec, &event->touch->base,\n\t\t\t\tdx, dy, dx, dy);\n\t\t}\n\t} else {\n\t\tseatop_touch_motion(seat, event, lx, ly);\n\t}\n}\n\nstatic void handle_touch_frame(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor =\n\t\twl_container_of(listener, cursor, touch_frame);\n\n\tstruct wlr_seat *wlr_seat = cursor->seat->wlr_seat;\n\n\tif (cursor->simulating_pointer_from_touch) {\n\t\twlr_seat_pointer_notify_frame(wlr_seat);\n\n\t\tif (cursor->pointer_touch_up) {\n\t\t\tcursor->pointer_touch_up = false;\n\t\t\tcursor->simulating_pointer_from_touch = false;\n\t\t}\n\t} else {\n\t\twlr_seat_touch_notify_frame(wlr_seat);\n\t}\n}\n\nstatic double apply_mapping_from_coord(double low, double high, double value) {\n\tif (isnan(value)) {\n\t\treturn value;\n\t}\n\n\treturn (value - low) / (high - low);\n}\n\nstatic void apply_mapping_from_region(struct wlr_input_device *device,\n\t\tstruct input_config_mapped_from_region *region, double *x, double *y) {\n\tdouble x1 = region->x1, x2 = region->x2;\n\tdouble y1 = region->y1, y2 = region->y2;\n\n\tif (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) {\n\t\tstruct wlr_tablet *tablet = wlr_tablet_from_input_device(device);\n\t\tif (tablet->width_mm == 0 || tablet->height_mm == 0) {\n\t\t\treturn;\n\t\t}\n\t\tx1 /= tablet->width_mm;\n\t\tx2 /= tablet->width_mm;\n\t\ty1 /= tablet->height_mm;\n\t\ty2 /= tablet->height_mm;\n\t}\n\n\t*x = apply_mapping_from_coord(x1, x2, *x);\n\t*y = apply_mapping_from_coord(y1, y2, *y);\n}\n\nstatic void handle_tablet_tool_position(struct sway_cursor *cursor,\n\t\tstruct sway_tablet_tool *tool,\n\t\tbool change_x, bool change_y,\n\t\tdouble x, double y, double dx, double dy,\n\t\tint32_t time_msec) {\n\n\tif (!change_x && !change_y) {\n\t\treturn;\n\t}\n\n\tstruct sway_tablet *tablet = tool->tablet;\n\tstruct sway_input_device *input_device = tablet->seat_device->input_device;\n\tstruct input_config *ic = input_device_get_config(input_device);\n\tif (ic != NULL && ic->mapped_from_region != NULL) {\n\t\tapply_mapping_from_region(input_device->wlr_device,\n\t\t\tic->mapped_from_region, &x, &y);\n\t}\n\n\tswitch (tool->mode) {\n\tcase SWAY_TABLET_TOOL_MODE_ABSOLUTE:\n\t\twlr_cursor_warp_absolute(cursor->cursor, input_device->wlr_device,\n\t\t\tchange_x ? x : NAN, change_y ? y : NAN);\n\t\tbreak;\n\tcase SWAY_TABLET_TOOL_MODE_RELATIVE:\n\t\twlr_cursor_move(cursor->cursor, input_device->wlr_device, dx, dy);\n\t\tbreak;\n\t}\n\n\tdouble sx, sy;\n\tstruct wlr_surface *surface = NULL;\n\tstruct sway_seat *seat = cursor->seat;\n\tnode_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\t// The logic for whether we should send a tablet event or an emulated pointer\n\t// event is tricky. It comes down to:\n\t// * If we began a drag on a non-tablet surface (simulating_pointer_from_tool_tip),\n\t//   then we should continue sending emulated pointer events regardless of\n\t//   whether the surface currently under us accepts tablet or not.\n\t// * Otherwise, if we are over a surface that accepts tablet, then we should\n\t//   send tablet events.\n\t// * If we began a drag over a tablet surface, we should continue sending\n\t//   tablet events until the drag is released, even if we are now over a\n\t//   non-tablet surface.\n\tif (!cursor->simulating_pointer_from_tool_tip &&\n\t\t\t((surface && wlr_surface_accepts_tablet_v2(surface, tablet->tablet_v2)) ||\n\t\t\t\twlr_tablet_tool_v2_has_implicit_grab(tool->tablet_v2_tool))) {\n\t\tseatop_tablet_tool_motion(seat, tool, time_msec);\n\t} else {\n\t\twlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);\n\t\tpointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy);\n\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t}\n}\n\nstatic void handle_tool_axis(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);\n\tstruct wlr_tablet_tool_axis_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->tablet->base);\n\n\tstruct sway_tablet_tool *sway_tool = event->tool->data;\n\tif (!sway_tool) {\n\t\tsway_log(SWAY_DEBUG, \"tool axis before proximity\");\n\t\treturn;\n\t}\n\n\thandle_tablet_tool_position(cursor, sway_tool,\n\t\tevent->updated_axes & WLR_TABLET_TOOL_AXIS_X,\n\t\tevent->updated_axes & WLR_TABLET_TOOL_AXIS_Y,\n\t\tevent->x, event->y, event->dx, event->dy, event->time_msec);\n\n\tif (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) {\n\t\twlr_tablet_v2_tablet_tool_notify_pressure(\n\t\t\tsway_tool->tablet_v2_tool, event->pressure);\n\t}\n\n\tif (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) {\n\t\twlr_tablet_v2_tablet_tool_notify_distance(\n\t\t\tsway_tool->tablet_v2_tool, event->distance);\n\t}\n\n\tif (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) {\n\t\tsway_tool->tilt_x = event->tilt_x;\n\t}\n\n\tif (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) {\n\t\tsway_tool->tilt_y = event->tilt_y;\n\t}\n\n\tif (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) {\n\t\twlr_tablet_v2_tablet_tool_notify_tilt(\n\t\t\tsway_tool->tablet_v2_tool,\n\t\t\tsway_tool->tilt_x, sway_tool->tilt_y);\n\t}\n\n\tif (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) {\n\t\twlr_tablet_v2_tablet_tool_notify_rotation(\n\t\t\tsway_tool->tablet_v2_tool, event->rotation);\n\t}\n\n\tif (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) {\n\t\twlr_tablet_v2_tablet_tool_notify_slider(\n\t\t\tsway_tool->tablet_v2_tool, event->slider);\n\t}\n\n\tif (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) {\n\t\twlr_tablet_v2_tablet_tool_notify_wheel(\n\t\t\tsway_tool->tablet_v2_tool, event->wheel_delta, 0);\n\t}\n}\n\nstatic void handle_tool_tip(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);\n\tstruct wlr_tablet_tool_tip_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->tablet->base);\n\n\tstruct sway_tablet_tool *sway_tool = event->tool->data;\n\tif (!sway_tool) {\n\t\tsway_log(SWAY_DEBUG, \"tool tip before proximity\");\n\t\treturn;\n\t}\n\n\tstruct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2;\n\tstruct sway_seat *seat = cursor->seat;\n\n\n\tdouble sx, sy;\n\tstruct wlr_surface *surface = NULL;\n\tnode_at_coords(seat, cursor->cursor->x, cursor->cursor->y,\n\t\t&surface, &sx, &sy);\n\n\tif (cursor->simulating_pointer_from_tool_tip &&\n\t\t\tevent->state == WLR_TABLET_TOOL_TIP_UP) {\n\t\tcursor->simulating_pointer_from_tool_tip = false;\n\t\tdispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,\n\t\t\tBTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);\n\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t} else if (!surface || !wlr_surface_accepts_tablet_v2(surface, tablet_v2)) {\n\t\t// If we started holding the tool tip down on a surface that accepts\n\t\t// tablet v2, we should notify that surface if it gets released over a\n\t\t// surface that doesn't support v2.\n\t\tif (event->state == WLR_TABLET_TOOL_TIP_UP) {\n\t\t\tseatop_tablet_tool_tip(seat, sway_tool, event->time_msec,\n\t\t\t\tWLR_TABLET_TOOL_TIP_UP);\n\t\t} else {\n\t\t\tcursor->simulating_pointer_from_tool_tip = true;\n\t\t\tdispatch_cursor_button(cursor, &event->tablet->base,\n\t\t\t\tevent->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);\n\t\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t\t}\n\t} else {\n\t\tseatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state);\n\t}\n}\n\nstatic struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor,\n\t\tstruct wlr_input_device *device) {\n\tstruct sway_tablet *tablet;\n\twl_list_for_each(tablet, &cursor->tablets, link) {\n\t\tif (tablet->seat_device->input_device->wlr_device == device) {\n\t\t\treturn tablet;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void handle_tool_proximity(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor =\n\t\twl_container_of(listener, cursor, tool_proximity);\n\tstruct wlr_tablet_tool_proximity_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->tablet->base);\n\n\tstruct wlr_tablet_tool *tool = event->tool;\n\tif (!tool->data) {\n\t\tstruct sway_tablet *tablet = get_tablet_for_device(cursor,\n\t\t\t&event->tablet->base);\n\t\tif (!tablet) {\n\t\t\tsway_log(SWAY_ERROR, \"no tablet for tablet tool\");\n\t\t\treturn;\n\t\t}\n\t\tsway_tablet_tool_configure(tablet, tool);\n\t}\n\n\tstruct sway_tablet_tool *sway_tool = tool->data;\n\tif (!sway_tool) {\n\t\tsway_log(SWAY_ERROR, \"tablet tool not initialized\");\n\t\treturn;\n\t}\n\n\tif (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {\n\t\twlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool);\n\t\treturn;\n\t}\n\n\thandle_tablet_tool_position(cursor, sway_tool, true, true, event->x, event->y,\n\t\t0, 0, event->time_msec);\n}\n\nstatic void handle_tool_button(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);\n\tstruct wlr_tablet_tool_button_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->tablet->base);\n\n\tstruct sway_tablet_tool *sway_tool = event->tool->data;\n\tif (!sway_tool) {\n\t\tsway_log(SWAY_DEBUG, \"tool button before proximity\");\n\t\treturn;\n\t}\n\tstruct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2;\n\n\tdouble sx, sy;\n\tstruct wlr_surface *surface = NULL;\n\n\tnode_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y,\n\t\t&surface, &sx, &sy);\n\n\t// TODO: floating resize should support graphics tablet events\n\tstruct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);\n\tuint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;\n\tbool mod_pressed = modifiers & config->floating_mod;\n\n\tbool surface_supports_tablet_events =\n\t\tsurface && wlr_surface_accepts_tablet_v2(surface, tablet_v2);\n\n\t// Simulate pointer when:\n\t// 1. The modifier key is pressed, OR\n\t// 2. The surface under the cursor does not support tablet events.\n\tbool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events;\n\n\t// Similar to tool tip, we need to selectively simulate mouse events, but we\n\t// want to make sure that it is always consistent. Because all tool buttons\n\t// currently map to BTN_RIGHT, we need to keep count of how many tool\n\t// buttons are currently pressed down so we can send consistent events.\n\t//\n\t// The logic follows:\n\t// - If we are already simulating the pointer, we should continue to do so\n\t//   until at least no tool button is held down.\n\t// - If we should simulate the pointer and no tool button is currently held\n\t//   down, begin simulating the pointer.\n\t// - If neither of the above are true, send the tablet events.\n\tif ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button)\n\t\t|| (cursor->tool_buttons == 0 && should_simulate_pointer)) {\n\t\tcursor->simulating_pointer_from_tool_button = true;\n\n\t\t// TODO: the user may want to configure which tool buttons are mapped to\n\t\t// which simulated pointer buttons\n\t\tswitch (event->state) {\n\t\tcase WLR_BUTTON_PRESSED:\n\t\t\tif (cursor->tool_buttons == 0) {\n\t\t\t\tdispatch_cursor_button(cursor, &event->tablet->base,\n\t\t\t\t\tevent->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase WLR_BUTTON_RELEASED:\n\t\t\tif (cursor->tool_buttons <= 1) {\n\t\t\t\tdispatch_cursor_button(cursor, &event->tablet->base,\n\t\t\t\t\tevent->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t} else {\n\t\tcursor->simulating_pointer_from_tool_button = false;\n\n\t\twlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool,\n\t\t\tevent->button, (enum zwp_tablet_pad_v2_button_state)event->state);\n\t}\n\n\t// Update tool button count.\n\tswitch (event->state) {\n\tcase WLR_BUTTON_PRESSED:\n\t\tcursor->tool_buttons++;\n\t\tbreak;\n\tcase WLR_BUTTON_RELEASED:\n\t\tif (cursor->tool_buttons == 0) {\n\t\t\tsway_log(SWAY_ERROR, \"inconsistent tablet tool button events\");\n\t\t} else {\n\t\t\tcursor->tool_buttons--;\n\t\t}\n\t\tbreak;\n\t}\n}\n\nstatic void check_constraint_region(struct sway_cursor *cursor) {\n\tstruct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;\n\tpixman_region32_t *region = &constraint->region;\n\tstruct sway_view *view = view_from_wlr_surface(constraint->surface);\n\tif (cursor->active_confine_requires_warp && view) {\n\t\tcursor->active_confine_requires_warp = false;\n\n\t\tstruct sway_container *con = view->container;\n\n\t\tdouble sx = cursor->cursor->x - con->pending.content_x + view->geometry.x;\n\t\tdouble sy = cursor->cursor->y - con->pending.content_y + view->geometry.y;\n\n\t\tif (!pixman_region32_contains_point(region,\n\t\t\t\tfloor(sx), floor(sy), NULL)) {\n\t\t\tint nboxes;\n\t\t\tpixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes);\n\t\t\tif (nboxes > 0) {\n\t\t\t\tdouble sx = (boxes[0].x1 + boxes[0].x2) / 2.;\n\t\t\t\tdouble sy = (boxes[0].y1 + boxes[0].y2) / 2.;\n\n\t\t\t\twlr_cursor_warp_closest(cursor->cursor, NULL,\n\t\t\t\t\tsx + con->pending.content_x - view->geometry.x,\n\t\t\t\t\tsy + con->pending.content_y - view->geometry.y);\n\n\t\t\t\tcursor_rebase(cursor);\n\t\t\t}\n\t\t}\n\t}\n\n\t// A locked pointer will result in an empty region, thus disallowing all movement\n\tif (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) {\n\t\tpixman_region32_copy(&cursor->confine, region);\n\t} else {\n\t\tpixman_region32_clear(&cursor->confine);\n\t}\n}\n\nstatic void handle_constraint_commit(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_cursor *cursor =\n\t\twl_container_of(listener, cursor, constraint_commit);\n\tstruct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;\n\tassert(constraint->surface == data);\n\n\tcheck_constraint_region(cursor);\n}\n\nstatic void handle_pointer_constraint_set_region(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_pointer_constraint *sway_constraint =\n\t\twl_container_of(listener, sway_constraint, set_region);\n\tstruct sway_cursor *cursor = sway_constraint->cursor;\n\n\tcursor->active_confine_requires_warp = true;\n}\n\nstatic void handle_request_pointer_set_cursor(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_cursor *cursor =\n\t\twl_container_of(listener, cursor, request_set_cursor);\n\tif (!seatop_allows_set_cursor(cursor->seat)) {\n\t\treturn;\n\t}\n\tstruct wlr_seat_pointer_request_set_cursor_event *event = data;\n\n\tstruct wl_client *focused_client = NULL;\n\tstruct wlr_surface *focused_surface =\n\t\tcursor->seat->wlr_seat->pointer_state.focused_surface;\n\tif (focused_surface != NULL) {\n\t\tfocused_client = wl_resource_get_client(focused_surface->resource);\n\t}\n\n\t// TODO: check cursor mode\n\tif (focused_client == NULL ||\n\t\t\tevent->seat_client->client != focused_client) {\n\t\tsway_log(SWAY_DEBUG, \"denying request to set cursor from unfocused client\");\n\t\treturn;\n\t}\n\n\tcursor_set_image_surface(cursor, event->surface, event->hotspot_x,\n\t\t\tevent->hotspot_y, focused_client);\n}\n\nstatic void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, hold_begin);\n\tstruct wlr_pointer_hold_begin_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_hold_begin(cursor->seat, event);\n}\n\nstatic void handle_pointer_hold_end(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, hold_end);\n\tstruct wlr_pointer_hold_end_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_hold_end(cursor->seat, event);\n}\n\nstatic void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, pinch_begin);\n\tstruct wlr_pointer_pinch_begin_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_pinch_begin(cursor->seat, event);\n}\n\nstatic void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, pinch_update);\n\tstruct wlr_pointer_pinch_update_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_pinch_update(cursor->seat, event);\n}\n\nstatic void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, pinch_end);\n\tstruct wlr_pointer_pinch_end_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_pinch_end(cursor->seat, event);\n}\n\nstatic void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, swipe_begin);\n\tstruct wlr_pointer_swipe_begin_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_swipe_begin(cursor->seat, event);\n}\n\nstatic void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, swipe_update);\n\tstruct wlr_pointer_swipe_update_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_swipe_update(cursor->seat, event);\n}\n\nstatic void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {\n\tstruct sway_cursor *cursor = wl_container_of(\n\t\t\tlistener, cursor, swipe_end);\n\tstruct wlr_pointer_swipe_end_event *event = data;\n\tcursor_handle_activity_from_device(cursor, &event->pointer->base);\n\tseatop_swipe_end(cursor->seat, event);\n}\n\nstatic void handle_image_surface_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_cursor *cursor =\n\t\twl_container_of(listener, cursor, image_surface_destroy);\n\tcursor_set_image(cursor, NULL, cursor->image_client);\n\tcursor_rebase(cursor);\n}\n\nstatic void set_image_surface(struct sway_cursor *cursor,\n\t\tstruct wlr_surface *surface) {\n\twl_list_remove(&cursor->image_surface_destroy.link);\n\tcursor->image_surface = surface;\n\tif (surface) {\n\t\twl_signal_add(&surface->events.destroy, &cursor->image_surface_destroy);\n\t} else {\n\t\twl_list_init(&cursor->image_surface_destroy.link);\n\t}\n}\n\nvoid cursor_set_image(struct sway_cursor *cursor, const char *image,\n\t\tstruct wl_client *client) {\n\tif (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {\n\t\treturn;\n\t}\n\n\tconst char *current_image = cursor->image;\n\tset_image_surface(cursor, NULL);\n\tcursor->image = image;\n\tcursor->hotspot_x = cursor->hotspot_y = 0;\n\tcursor->image_client = client;\n\n\tif (cursor->hidden) {\n\t\treturn;\n\t}\n\n\tif (!image) {\n\t\twlr_cursor_unset_image(cursor->cursor);\n\t} else if (!current_image || strcmp(current_image, image) != 0) {\n\t\twlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image);\n\t}\n}\n\nvoid cursor_set_image_surface(struct sway_cursor *cursor,\n\t\tstruct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y,\n\t\tstruct wl_client *client) {\n\tif (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {\n\t\treturn;\n\t}\n\n\tset_image_surface(cursor, surface);\n\tcursor->image = NULL;\n\tcursor->hotspot_x = hotspot_x;\n\tcursor->hotspot_y = hotspot_y;\n\tcursor->image_client = client;\n\n\tif (cursor->hidden) {\n\t\treturn;\n\t}\n\n\twlr_cursor_set_surface(cursor->cursor, surface, hotspot_x, hotspot_y);\n}\n\nvoid sway_cursor_destroy(struct sway_cursor *cursor) {\n\tif (!cursor) {\n\t\treturn;\n\t}\n\n\twl_event_source_remove(cursor->hide_source);\n\n\twl_list_remove(&cursor->image_surface_destroy.link);\n\twl_list_remove(&cursor->hold_begin.link);\n\twl_list_remove(&cursor->hold_end.link);\n\twl_list_remove(&cursor->pinch_begin.link);\n\twl_list_remove(&cursor->pinch_update.link);\n\twl_list_remove(&cursor->pinch_end.link);\n\twl_list_remove(&cursor->swipe_begin.link);\n\twl_list_remove(&cursor->swipe_update.link);\n\twl_list_remove(&cursor->swipe_end.link);\n\twl_list_remove(&cursor->motion.link);\n\twl_list_remove(&cursor->motion_absolute.link);\n\twl_list_remove(&cursor->button.link);\n\twl_list_remove(&cursor->axis.link);\n\twl_list_remove(&cursor->frame.link);\n\twl_list_remove(&cursor->touch_down.link);\n\twl_list_remove(&cursor->touch_up.link);\n\twl_list_remove(&cursor->touch_cancel.link);\n\twl_list_remove(&cursor->touch_motion.link);\n\twl_list_remove(&cursor->touch_frame.link);\n\twl_list_remove(&cursor->tool_axis.link);\n\twl_list_remove(&cursor->tool_tip.link);\n\twl_list_remove(&cursor->tool_proximity.link);\n\twl_list_remove(&cursor->tool_button.link);\n\twl_list_remove(&cursor->request_set_cursor.link);\n\n\twlr_xcursor_manager_destroy(cursor->xcursor_manager);\n\twlr_cursor_destroy(cursor->cursor);\n\tfree(cursor);\n}\n\nstruct sway_cursor *sway_cursor_create(struct sway_seat *seat) {\n\tstruct sway_cursor *cursor = calloc(1, sizeof(struct sway_cursor));\n\tif (!sway_assert(cursor, \"could not allocate sway cursor\")) {\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_cursor *wlr_cursor = wlr_cursor_create();\n\tif (!sway_assert(wlr_cursor, \"could not allocate wlr cursor\")) {\n\t\tfree(cursor);\n\t\treturn NULL;\n\t}\n\n\tcursor->previous.x = wlr_cursor->x;\n\tcursor->previous.y = wlr_cursor->y;\n\n\tcursor->seat = seat;\n\twlr_cursor_attach_output_layout(wlr_cursor, root->output_layout);\n\n\tcursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop,\n\t\t\thide_notify, cursor);\n\n\twl_list_init(&cursor->image_surface_destroy.link);\n\tcursor->image_surface_destroy.notify = handle_image_surface_destroy;\n\n\twl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);\n\tcursor->hold_begin.notify = handle_pointer_hold_begin;\n\twl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);\n\tcursor->hold_end.notify = handle_pointer_hold_end;\n\n\twl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);\n\tcursor->pinch_begin.notify = handle_pointer_pinch_begin;\n\twl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);\n\tcursor->pinch_update.notify = handle_pointer_pinch_update;\n\twl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);\n\tcursor->pinch_end.notify = handle_pointer_pinch_end;\n\n\twl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);\n\tcursor->swipe_begin.notify = handle_pointer_swipe_begin;\n\twl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);\n\tcursor->swipe_update.notify = handle_pointer_swipe_update;\n\twl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);\n\tcursor->swipe_end.notify = handle_pointer_swipe_end;\n\n\t// input events\n\twl_signal_add(&wlr_cursor->events.motion, &cursor->motion);\n\tcursor->motion.notify = handle_pointer_motion_relative;\n\n\twl_signal_add(&wlr_cursor->events.motion_absolute,\n\t\t&cursor->motion_absolute);\n\tcursor->motion_absolute.notify = handle_pointer_motion_absolute;\n\n\twl_signal_add(&wlr_cursor->events.button, &cursor->button);\n\tcursor->button.notify = handle_pointer_button;\n\n\twl_signal_add(&wlr_cursor->events.axis, &cursor->axis);\n\tcursor->axis.notify = handle_pointer_axis;\n\n\twl_signal_add(&wlr_cursor->events.frame, &cursor->frame);\n\tcursor->frame.notify = handle_pointer_frame;\n\n\twl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down);\n\tcursor->touch_down.notify = handle_touch_down;\n\n\twl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);\n\tcursor->touch_up.notify = handle_touch_up;\n\n\twl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel);\n\tcursor->touch_cancel.notify = handle_touch_cancel;\n\n\twl_signal_add(&wlr_cursor->events.touch_motion,\n\t\t&cursor->touch_motion);\n\tcursor->touch_motion.notify = handle_touch_motion;\n\n\twl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame);\n\tcursor->touch_frame.notify = handle_touch_frame;\n\n\twl_signal_add(&wlr_cursor->events.tablet_tool_axis,\n\t\t&cursor->tool_axis);\n\tcursor->tool_axis.notify = handle_tool_axis;\n\n\twl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);\n\tcursor->tool_tip.notify = handle_tool_tip;\n\n\twl_signal_add(&wlr_cursor->events.tablet_tool_proximity, &cursor->tool_proximity);\n\tcursor->tool_proximity.notify = handle_tool_proximity;\n\n\twl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button);\n\tcursor->tool_button.notify = handle_tool_button;\n\n\twl_signal_add(&seat->wlr_seat->events.request_set_cursor,\n\t\t\t&cursor->request_set_cursor);\n\tcursor->request_set_cursor.notify = handle_request_pointer_set_cursor;\n\n\twl_list_init(&cursor->constraint_commit.link);\n\twl_list_init(&cursor->tablets);\n\twl_list_init(&cursor->tablet_pads);\n\n\tcursor->cursor = wlr_cursor;\n\n\treturn cursor;\n}\n\n/**\n * Warps the cursor to the middle of the container argument.\n * Does nothing if the cursor is already inside the container and `force` is\n * false. If container is NULL, returns without doing anything.\n */\nvoid cursor_warp_to_container(struct sway_cursor *cursor,\n\t\tstruct sway_container *container, bool force) {\n\tif (!container) {\n\t\treturn;\n\t}\n\n\tstruct wlr_box box;\n\tcontainer_get_box(container, &box);\n\tif (!force && wlr_box_contains_point(&box, cursor->cursor->x,\n\t\t\tcursor->cursor->y)) {\n\t\treturn;\n\t}\n\n\tdouble x = container->pending.x + container->pending.width / 2.0;\n\tdouble y = container->pending.y + container->pending.height / 2.0;\n\n\twlr_cursor_warp(cursor->cursor, NULL, x, y);\n\tcursor_unhide(cursor);\n}\n\n/**\n * Warps the cursor to the middle of the workspace argument.\n * If workspace is NULL, returns without doing anything.\n */\nvoid cursor_warp_to_workspace(struct sway_cursor *cursor,\n\t\tstruct sway_workspace *workspace) {\n\tif (!workspace) {\n\t\treturn;\n\t}\n\n\tdouble x = workspace->x + workspace->width / 2.0;\n\tdouble y = workspace->y + workspace->height / 2.0;\n\n\twlr_cursor_warp(cursor->cursor, NULL, x, y);\n\tcursor_unhide(cursor);\n}\n\nuint32_t get_mouse_bindsym(const char *name, char **error) {\n\tif (strncasecmp(name, \"button\", strlen(\"button\")) == 0) {\n\t\t// Map to x11 mouse buttons\n\t\tint number = name[strlen(\"button\")] - '0';\n\t\tif (number < 1 || number > 9 || strlen(name) > strlen(\"button0\")) {\n\t\t\t*error = strdup(\"Only buttons 1-9 are supported. For other mouse \"\n\t\t\t\t\t\"buttons, use the name of the event code.\");\n\t\t\treturn 0;\n\t\t}\n\t\tstatic const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,\n\t\t\tSWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT,\n\t\t\tSWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA};\n\t\treturn buttons[number - 1];\n\t} else if (has_prefix(name, \"BTN_\")) {\n\t\t// Get event code from name\n\t\tint code = libevdev_event_code_from_name(EV_KEY, name);\n\t\tif (code == -1) {\n\t\t\t*error = format_str(\"Unknown event %s\", name);\n\t\t\treturn 0;\n\t\t}\n\t\treturn code;\n\t}\n\treturn 0;\n}\n\nuint32_t get_mouse_bindcode(const char *name, char **error) {\n\t// Validate event code\n\terrno = 0;\n\tchar *endptr;\n\tint code = strtol(name, &endptr, 10);\n\tif (endptr == name && code <= 0) {\n\t\t*error = strdup(\"Button event code must be a positive integer.\");\n\t\treturn 0;\n\t} else if (errno == ERANGE) {\n\t\t*error = strdup(\"Button event code out of range.\");\n\t\treturn 0;\n\t}\n\tconst char *event = libevdev_event_code_get_name(EV_KEY, code);\n\tif (!event || !has_prefix(event, \"BTN_\")) {\n\t\t*error = format_str(\"Event code %d (%s) is not a button\",\n\t\t\tcode, event ? event : \"(null)\");\n\t\treturn 0;\n\t}\n\treturn code;\n}\n\nuint32_t get_mouse_button(const char *name, char **error) {\n\tuint32_t button = get_mouse_bindsym(name, error);\n\tif (!button && !*error) {\n\t\tbutton = get_mouse_bindcode(name, error);\n\t}\n\treturn button;\n}\n\nconst char *get_mouse_button_name(uint32_t button) {\n\tconst char *name = libevdev_event_code_get_name(EV_KEY, button);\n\tif (!name) {\n\t\tif (button == SWAY_SCROLL_UP) {\n\t\t\tname = \"SWAY_SCROLL_UP\";\n\t\t} else if (button == SWAY_SCROLL_DOWN) {\n\t\t\tname = \"SWAY_SCROLL_DOWN\";\n\t\t} else if (button == SWAY_SCROLL_LEFT) {\n\t\t\tname = \"SWAY_SCROLL_LEFT\";\n\t\t} else if (button == SWAY_SCROLL_RIGHT) {\n\t\t\tname = \"SWAY_SCROLL_RIGHT\";\n\t\t}\n\t}\n\treturn name;\n}\n\nstatic void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {\n\tstruct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;\n\n\tif (constraint->current.cursor_hint.enabled) {\n\t\tdouble sx = constraint->current.cursor_hint.x;\n\t\tdouble sy = constraint->current.cursor_hint.y;\n\n\t\tstruct sway_view *view = view_from_wlr_surface(constraint->surface);\n\t\tif (!view) {\n\t\t\treturn;\n\t\t}\n\n\t\tstruct sway_container *con = view->container;\n\n\t\tdouble lx = sx + con->pending.content_x - view->geometry.x;\n\t\tdouble ly = sy + con->pending.content_y - view->geometry.y;\n\n\t\twlr_cursor_warp(cursor->cursor, NULL, lx, ly);\n\n\t\t// Warp the pointer as well, so that on the next pointer rebase we don't\n\t\t// send an unexpected synthetic motion event to clients.\n\t\twlr_seat_pointer_warp(constraint->seat, sx, sy);\n\t}\n}\n\nvoid handle_constraint_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_pointer_constraint *sway_constraint =\n\t\twl_container_of(listener, sway_constraint, destroy);\n\tstruct wlr_pointer_constraint_v1 *constraint = data;\n\tstruct sway_cursor *cursor = sway_constraint->cursor;\n\n\twl_list_remove(&sway_constraint->set_region.link);\n\twl_list_remove(&sway_constraint->destroy.link);\n\n\tif (cursor->active_constraint == constraint) {\n\t\twarp_to_constraint_cursor_hint(cursor);\n\n\t\tif (cursor->constraint_commit.link.next != NULL) {\n\t\t\twl_list_remove(&cursor->constraint_commit.link);\n\t\t}\n\t\twl_list_init(&cursor->constraint_commit.link);\n\t\tcursor->active_constraint = NULL;\n\t}\n\n\tfree(sway_constraint);\n}\n\nvoid handle_pointer_constraint(struct wl_listener *listener, void *data) {\n\tstruct wlr_pointer_constraint_v1 *constraint = data;\n\tstruct sway_seat *seat = constraint->seat->data;\n\n\tstruct sway_pointer_constraint *sway_constraint =\n\t\tcalloc(1, sizeof(struct sway_pointer_constraint));\n\tsway_constraint->cursor = seat->cursor;\n\tsway_constraint->constraint = constraint;\n\n\tsway_constraint->set_region.notify = handle_pointer_constraint_set_region;\n\twl_signal_add(&constraint->events.set_region, &sway_constraint->set_region);\n\n\tsway_constraint->destroy.notify = handle_constraint_destroy;\n\twl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);\n\n\tstruct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;\n\tif (surface && surface == constraint->surface) {\n\t\tsway_cursor_constrain(seat->cursor, constraint);\n\t}\n}\n\nvoid sway_cursor_constrain(struct sway_cursor *cursor,\n\t\tstruct wlr_pointer_constraint_v1 *constraint) {\n\tstruct seat_config *config = seat_get_config(cursor->seat);\n\tif (!config) {\n\t\tconfig = seat_get_config_by_name(\"*\");\n\t}\n\n\tif (!config || config->allow_constrain == CONSTRAIN_DISABLE) {\n\t\treturn;\n\t}\n\n\tif (cursor->active_constraint == constraint) {\n\t\treturn;\n\t}\n\n\twl_list_remove(&cursor->constraint_commit.link);\n\tif (cursor->active_constraint) {\n\t\tif (constraint == NULL) {\n\t\t\twarp_to_constraint_cursor_hint(cursor);\n\t\t}\n\t\twlr_pointer_constraint_v1_send_deactivated(\n\t\t\tcursor->active_constraint);\n\t}\n\n\tcursor->active_constraint = constraint;\n\n\tif (constraint == NULL) {\n\t\twl_list_init(&cursor->constraint_commit.link);\n\t\treturn;\n\t}\n\n\tcursor->active_confine_requires_warp = true;\n\n\t// FIXME: Big hack, stolen from wlr_pointer_constraints_v1.c:121.\n\t// This is necessary because the focus may be set before the surface\n\t// has finished committing, which means that warping won't work properly,\n\t// since this code will be run *after* the focus has been set.\n\t// That is why we duplicate the code here.\n\tif (pixman_region32_not_empty(&constraint->current.region)) {\n\t\tpixman_region32_intersect(&constraint->region,\n\t\t\t&constraint->surface->input_region, &constraint->current.region);\n\t} else {\n\t\tpixman_region32_copy(&constraint->region,\n\t\t\t&constraint->surface->input_region);\n\t}\n\n\tcheck_constraint_region(cursor);\n\n\twlr_pointer_constraint_v1_send_activated(constraint);\n\n\tcursor->constraint_commit.notify = handle_constraint_commit;\n\twl_signal_add(&constraint->surface->events.commit,\n\t\t&cursor->constraint_commit);\n}\n\nvoid handle_request_set_cursor_shape(struct wl_listener *listener, void *data) {\n\tconst struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;\n\tstruct sway_seat *seat = event->seat_client->seat->data;\n\n\tif (!seatop_allows_set_cursor(seat)) {\n\t\treturn;\n\t}\n\n\tstruct wl_client *focused_client = NULL;\n\tstruct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface;\n\tif (focused_surface != NULL) {\n\t\tfocused_client = wl_resource_get_client(focused_surface->resource);\n\t}\n\n\t// TODO: check cursor mode\n\tif (focused_client == NULL || event->seat_client->client != focused_client) {\n\t\tsway_log(SWAY_DEBUG, \"denying request to set cursor from unfocused client\");\n\t\treturn;\n\t}\n\n\tcursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client);\n}\n"
  },
  {
    "path": "sway/input/input-manager.c",
    "content": "#include <ctype.h>\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n#include <assert.h>\n#include <wlr/config.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_keyboard_group.h>\n#include <wlr/types/wlr_virtual_keyboard_v1.h>\n#include <wlr/types/wlr_virtual_pointer_v1.h>\n#include \"sway/config.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/input/libinput.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/view.h\"\n#include \"stringop.h\"\n#include \"list.h\"\n#include \"log.h\"\n\n#if WLR_HAS_LIBINPUT_BACKEND\n#include <wlr/backend/libinput.h>\n#endif\n\n#define DEFAULT_SEAT \"seat0\"\n\nstruct input_config *current_input_config = NULL;\nstruct seat_config *current_seat_config = NULL;\n\nstruct sway_seat *input_manager_current_seat(void) {\n\tstruct sway_seat *seat = config->handler_context.seat;\n\tif (!seat) {\n\t\tseat = input_manager_get_default_seat();\n\t}\n\treturn seat;\n}\n\nstruct sway_seat *input_manager_get_default_seat(void) {\n\treturn input_manager_get_seat(DEFAULT_SEAT, true);\n}\n\nstruct sway_seat *input_manager_get_seat(const char *seat_name, bool create) {\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tif (strcmp(seat->wlr_seat->name, seat_name) == 0) {\n\t\t\treturn seat;\n\t\t}\n\t}\n\n\treturn create ? seat_create(seat_name) : NULL;\n}\n\nstruct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_seat) {\n\tstruct sway_seat *seat = NULL;\n\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tif (seat->wlr_seat == wlr_seat) {\n\t\t\treturn seat;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nchar *input_device_get_identifier(struct wlr_input_device *device) {\n\tint vendor = 0, product = 0;\n#if WLR_HAS_LIBINPUT_BACKEND\n\tif (wlr_input_device_is_libinput(device)) {\n\t\tstruct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device);\n\t\tvendor = libinput_device_get_id_vendor(libinput_dev);\n\t\tproduct = libinput_device_get_id_product(libinput_dev);\n\t}\n#endif\n\n\tchar *name = strdup(device->name ? device->name : \"\");\n\tstrip_whitespace(name);\n\n\tchar *p = name;\n\tfor (; *p; ++p) {\n\t\t// There are in fact input devices with unprintable characters in its name\n\t\tif (*p == ' ' || !isprint(*p)) {\n\t\t\t*p = '_';\n\t\t}\n\t}\n\n\tchar *identifier = format_str(\"%d:%d:%s\", vendor, product, name);\n\tfree(name);\n\treturn identifier;\n}\n\nstatic bool device_is_touchpad(struct sway_input_device *device) {\n#if WLR_HAS_LIBINPUT_BACKEND\n\tif (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER ||\n\t\t\t!wlr_input_device_is_libinput(device->wlr_device)) {\n\t\treturn false;\n\t}\n\n\tstruct libinput_device *libinput_device =\n\t\twlr_libinput_get_device_handle(device->wlr_device);\n\n\treturn libinput_device_config_tap_get_finger_count(libinput_device) > 0;\n#else\n\treturn false;\n#endif\n}\n\nconst char *input_device_get_type(struct sway_input_device *device) {\n\tswitch (device->wlr_device->type) {\n\tcase WLR_INPUT_DEVICE_POINTER:\n\t\tif (device_is_touchpad(device)) {\n\t\t\treturn \"touchpad\";\n\t\t} else {\n\t\t\treturn \"pointer\";\n\t\t}\n\tcase WLR_INPUT_DEVICE_KEYBOARD:\n\t\treturn \"keyboard\";\n\tcase WLR_INPUT_DEVICE_TOUCH:\n\t\treturn \"touch\";\n\tcase WLR_INPUT_DEVICE_TABLET:\n\t\treturn \"tablet_tool\";\n\tcase WLR_INPUT_DEVICE_TABLET_PAD:\n\t\treturn \"tablet_pad\";\n\tcase WLR_INPUT_DEVICE_SWITCH:\n\t\treturn \"switch\";\n\t}\n\treturn \"unknown\";\n}\n\nstatic void apply_input_type_config(struct sway_input_device *input_device) {\n\tconst char *device_type = input_device_get_type(input_device);\n\tstruct input_config *type_config = NULL;\n\tfor (int i = 0; i < config->input_type_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_type_configs->items[i];\n\t\tif (strcmp(ic->identifier + 5, device_type) == 0) {\n\t\t\ttype_config = ic;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (type_config == NULL) {\n\t\treturn;\n\t}\n\n\tfor (int i = 0; i < config->input_configs->length; i++) {\n\t\tstruct input_config *ic = config->input_configs->items[i];\n\t\tif (strcmp(input_device->identifier, ic->identifier) == 0) {\n\t\t\tstruct input_config *current = new_input_config(ic->identifier);\n\t\t\tmerge_input_config(current, type_config);\n\t\t\tmerge_input_config(current, ic);\n\n\t\t\tcurrent->input_type = device_type;\n\t\t\tconfig->input_configs->items[i] = current;\n\t\t\tfree_input_config(ic);\n\t\t\tic = NULL;\n\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic struct sway_input_device *input_sway_device_from_wlr(\n\t\tstruct wlr_input_device *device) {\n\tstruct sway_input_device *input_device = NULL;\n\twl_list_for_each(input_device, &server.input->devices, link) {\n\t\tif (input_device->wlr_device == device) {\n\t\t\treturn input_device;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic bool input_has_seat_fallback_configuration(void) {\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tstruct seat_config *seat_config = seat_get_config(seat);\n\t\tif (seat_config && strcmp(seat_config->name, \"*\") != 0\n\t\t\t\t&& seat_config->fallback != -1) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid input_manager_verify_fallback_seat(void) {\n\tstruct sway_seat *seat = NULL;\n\tif (!input_has_seat_fallback_configuration()) {\n\t\tsway_log(SWAY_DEBUG, \"no fallback seat config - creating default\");\n\t\tseat = input_manager_get_default_seat();\n\t\tstruct seat_config *sc = new_seat_config(seat->wlr_seat->name);\n\t\tsc->fallback = true;\n\t\tsc = store_seat_config(sc);\n\t\tinput_manager_apply_seat_config(sc);\n\t}\n}\n\nstatic void handle_device_destroy(struct wl_listener *listener, void *data) {\n\tstruct wlr_input_device *device = data;\n\n\tstruct sway_input_device *input_device = input_sway_device_from_wlr(device);\n\n\tif (!sway_assert(input_device, \"could not find sway device\")) {\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"removing device: '%s'\",\n\t\tinput_device->identifier);\n\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat_remove_device(seat, input_device);\n\t}\n\n\tipc_event_input(\"removed\", input_device);\n\n\twl_list_remove(&input_device->link);\n\twl_list_remove(&input_device->device_destroy.link);\n\tfree(input_device->identifier);\n\tfree(input_device);\n}\n\nstatic void handle_new_input(struct wl_listener *listener, void *data) {\n\tstruct sway_input_manager *input =\n\t\twl_container_of(listener, input, new_input);\n\tstruct wlr_input_device *device = data;\n\n\tstruct sway_input_device *input_device =\n\t\tcalloc(1, sizeof(struct sway_input_device));\n\tif (!sway_assert(input_device, \"could not allocate input device\")) {\n\t\treturn;\n\t}\n\tdevice->data = input_device;\n\n\tinput_device->wlr_device = device;\n\tinput_device->identifier = input_device_get_identifier(device);\n\twl_list_insert(&input->devices, &input_device->link);\n\n\tsway_log(SWAY_DEBUG, \"adding device: '%s'\",\n\t\tinput_device->identifier);\n\n\tapply_input_type_config(input_device);\n\n#if WLR_HAS_LIBINPUT_BACKEND\n\tbool config_changed = sway_input_configure_libinput_device(input_device);\n#else\n\tbool config_changed = false;\n#endif\n\n\twl_signal_add(&device->events.destroy, &input_device->device_destroy);\n\tinput_device->device_destroy.notify = handle_device_destroy;\n\n\tinput_manager_verify_fallback_seat();\n\n\tbool added = false;\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &input->seats, link) {\n\t\tstruct seat_config *seat_config = seat_get_config(seat);\n\t\tbool has_attachment = seat_config &&\n\t\t\t(seat_config_get_attachment(seat_config, input_device->identifier) ||\n\t\t\t seat_config_get_attachment(seat_config, \"*\"));\n\n\t\tif (has_attachment) {\n\t\t\tseat_add_device(seat, input_device);\n\t\t\tadded = true;\n\t\t}\n\t}\n\n\tif (!added) {\n\t\twl_list_for_each(seat, &input->seats, link) {\n\t\t\tstruct seat_config *seat_config = seat_get_config(seat);\n\t\t\tif (seat_config && seat_config->fallback == 1) {\n\t\t\t\tseat_add_device(seat, input_device);\n\t\t\t\tadded = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!added) {\n\t\tsway_log(SWAY_DEBUG,\n\t\t\t\"device '%s' is not configured on any seats\",\n\t\t\tinput_device->identifier);\n\t}\n\n\tipc_event_input(\"added\", input_device);\n\n\tif (config_changed) {\n\t\tipc_event_input(\"libinput_config\", input_device);\n\t}\n}\n\nstatic void handle_keyboard_shortcuts_inhibitor_destroy(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =\n\t\twl_container_of(listener, sway_inhibitor, destroy);\n\n\tsway_log(SWAY_DEBUG, \"Removing keyboard shortcuts inhibitor\");\n\n\t// sway_seat::keyboard_shortcuts_inhibitors\n\twl_list_remove(&sway_inhibitor->link);\n\twl_list_remove(&sway_inhibitor->destroy.link);\n\tfree(sway_inhibitor);\n}\n\nstatic void handle_keyboard_shortcuts_inhibit_new_inhibitor(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_input_manager *input_manager =\n\t\twl_container_of(listener, input_manager,\n\t\t\t\tkeyboard_shortcuts_inhibit_new_inhibitor);\n\tstruct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = data;\n\n\tsway_log(SWAY_DEBUG, \"Adding keyboard shortcuts inhibitor\");\n\n\tstruct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =\n\t\tcalloc(1, sizeof(struct sway_keyboard_shortcuts_inhibitor));\n\tif (!sway_assert(sway_inhibitor, \"could not allocate keyboard \"\n\t\t\t\t\"shortcuts inhibitor\")) {\n\t\treturn;\n\t}\n\tsway_inhibitor->inhibitor = inhibitor;\n\n\tsway_inhibitor->destroy.notify = handle_keyboard_shortcuts_inhibitor_destroy;\n\twl_signal_add(&inhibitor->events.destroy, &sway_inhibitor->destroy);\n\n\t// attach inhibitor to the seat it applies to\n\tstruct sway_seat *seat = inhibitor->seat->data;\n\twl_list_insert(&seat->keyboard_shortcuts_inhibitors, &sway_inhibitor->link);\n\n\t// per-view, seat-agnostic config via criteria\n\tstruct sway_view *view = view_from_wlr_surface(inhibitor->surface);\n\tenum seat_config_shortcuts_inhibit inhibit = SHORTCUTS_INHIBIT_DEFAULT;\n\tif (view) {\n\t\tinhibit = view->shortcuts_inhibit;\n\t}\n\n\tif (inhibit == SHORTCUTS_INHIBIT_DEFAULT) {\n\t\tstruct seat_config *config = seat_get_config(seat);\n\t\tif (!config) {\n\t\t\tconfig = seat_get_config_by_name(\"*\");\n\t\t}\n\n\t\tif (config) {\n\t\t\tinhibit = config->shortcuts_inhibit;\n\t\t}\n\t}\n\n\tif (inhibit == SHORTCUTS_INHIBIT_DISABLE) {\n\t\t/**\n\t\t * Here we deny to honour the inhibitor by never sending the\n\t\t * activate signal. We can not, however, destroy the inhibitor\n\t\t * because the protocol doesn't allow for it. So it will linger\n\t\t * until the client removes it im- or explicitly. But at least\n\t\t * it can only be one inhibitor per surface and seat at a time.\n\t\t *\n\t\t * We also want to allow the user to activate the inhibitor\n\t\t * manually later which is why we do this check here where the\n\t\t * inhibitor is already attached to its seat and ready for use.\n\t\t */\n\t\treturn;\n\t}\n\n\twlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor);\n}\n\nvoid handle_virtual_keyboard(struct wl_listener *listener, void *data) {\n\tstruct sway_input_manager *input_manager =\n\t\twl_container_of(listener, input_manager, virtual_keyboard_new);\n\tstruct wlr_virtual_keyboard_v1 *keyboard = data;\n\tstruct wlr_input_device *device = &keyboard->keyboard.base;\n\n\t// TODO: Amend protocol to allow NULL seat\n\tstruct sway_seat *seat = keyboard->seat ?\n\t\tinput_manager_sway_seat_from_wlr_seat(keyboard->seat) :\n\t\tinput_manager_get_default_seat();\n\n\tstruct sway_input_device *input_device =\n\t\tcalloc(1, sizeof(struct sway_input_device));\n\tif (!sway_assert(input_device, \"could not allocate input device\")) {\n\t\treturn;\n\t}\n\tdevice->data = input_device;\n\n\tinput_device->is_virtual = true;\n\tinput_device->wlr_device = device;\n\tinput_device->identifier = input_device_get_identifier(device);\n\twl_list_insert(&input_manager->devices, &input_device->link);\n\n\tsway_log(SWAY_DEBUG, \"adding virtual keyboard: '%s'\",\n\t\tinput_device->identifier);\n\n\twl_signal_add(&device->events.destroy, &input_device->device_destroy);\n\tinput_device->device_destroy.notify = handle_device_destroy;\n\n\tseat_add_device(seat, input_device);\n}\n\nvoid handle_virtual_pointer(struct wl_listener *listener, void *data) {\n\tstruct sway_input_manager *input_manager =\n\t\twl_container_of(listener, input_manager, virtual_pointer_new);\n\tstruct wlr_virtual_pointer_v1_new_pointer_event *event = data;\n\tstruct wlr_virtual_pointer_v1 *pointer = event->new_pointer;\n\tstruct wlr_input_device *device = &pointer->pointer.base;\n\n\tstruct sway_seat *seat = event->suggested_seat ?\n\t\tinput_manager_sway_seat_from_wlr_seat(event->suggested_seat) :\n\t\tinput_manager_get_default_seat();\n\n\tstruct sway_input_device *input_device =\n\t\tcalloc(1, sizeof(struct sway_input_device));\n\tif (!sway_assert(input_device, \"could not allocate input device\")) {\n\t\treturn;\n\t}\n\tdevice->data = input_device;\n\n\tinput_device->is_virtual = true;\n\tinput_device->wlr_device = device;\n\tinput_device->identifier = input_device_get_identifier(device);\n\twl_list_insert(&input_manager->devices, &input_device->link);\n\n\tsway_log(SWAY_DEBUG, \"adding virtual pointer: '%s'\",\n\t\tinput_device->identifier);\n\n\twl_signal_add(&device->events.destroy, &input_device->device_destroy);\n\tinput_device->device_destroy.notify = handle_device_destroy;\n\n\tseat_add_device(seat, input_device);\n\n\tif (event->suggested_output) {\n\t\twlr_cursor_map_input_to_output(seat->cursor->cursor, device,\n\t\t\tevent->suggested_output);\n\t}\n}\n\nstatic void handle_transient_seat_manager_create_seat(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct wlr_transient_seat_v1 *transient_seat = data;\n\tstatic uint64_t i;\n\tchar name[256];\n\tsnprintf(name, sizeof(name), \"transient-%\"PRIx64, i++);\n\tstruct sway_seat *seat = seat_create(name);\n\tif (seat && seat->wlr_seat) {\n\t\twlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat);\n\t} else {\n\t\twlr_transient_seat_v1_deny(transient_seat);\n\t}\n}\n\nstruct sway_input_manager *input_manager_create(struct sway_server *server) {\n\tstruct sway_input_manager *input =\n\t\tcalloc(1, sizeof(struct sway_input_manager));\n\tif (!input) {\n\t\treturn NULL;\n\t}\n\n\twl_list_init(&input->devices);\n\twl_list_init(&input->seats);\n\n\tinput->new_input.notify = handle_new_input;\n\twl_signal_add(&server->backend->events.new_input, &input->new_input);\n\n\tinput->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(\n\t\tserver->wl_display);\n\twl_signal_add(&input->virtual_keyboard->events.new_virtual_keyboard,\n\t\t&input->virtual_keyboard_new);\n\tinput->virtual_keyboard_new.notify = handle_virtual_keyboard;\n\n\tinput->virtual_pointer = wlr_virtual_pointer_manager_v1_create(\n\t\tserver->wl_display\n\t);\n\twl_signal_add(&input->virtual_pointer->events.new_virtual_pointer,\n\t\t&input->virtual_pointer_new);\n\tinput->virtual_pointer_new.notify = handle_virtual_pointer;\n\n\tinput->keyboard_shortcuts_inhibit =\n\t\twlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display);\n\tinput->keyboard_shortcuts_inhibit_new_inhibitor.notify =\n\t\thandle_keyboard_shortcuts_inhibit_new_inhibitor;\n\twl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor,\n\t\t\t&input->keyboard_shortcuts_inhibit_new_inhibitor);\n\n\tinput->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display);\n\n\tinput->transient_seat_manager =\n\t\twlr_transient_seat_manager_v1_create(server->wl_display);\n\tassert(input->transient_seat_manager);\n\n\tinput->transient_seat_create.notify =\n\t\thandle_transient_seat_manager_create_seat;\n\twl_signal_add(&input->transient_seat_manager->events.create_seat,\n\t\t\t&input->transient_seat_create);\n\n\treturn input;\n}\n\nvoid input_manager_finish(struct sway_input_manager *input) {\n\twl_list_remove(&input->new_input.link);\n\twl_list_remove(&input->virtual_keyboard_new.link);\n\twl_list_remove(&input->virtual_pointer_new.link);\n\twl_list_remove(&input->keyboard_shortcuts_inhibit_new_inhibitor.link);\n\twl_list_remove(&input->transient_seat_create.link);\n}\n\nbool input_manager_has_focus(struct sway_node *node) {\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tif (seat_get_focus(seat) == node) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid input_manager_set_focus(struct sway_node *node) {\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat_set_focus(seat, node);\n\t\tseat_consider_warp_to_focus(seat);\n\t}\n}\n\n/**\n * Re-translate keysyms if a change in the input config could affect them.\n */\nstatic void retranslate_keysyms(struct input_config *input_config) {\n\tfor (int i = 0; i < config->input_configs->length; ++i) {\n\t\tstruct input_config *ic = config->input_configs->items[i];\n\t\tif (ic->xkb_layout || ic->xkb_file) {\n\t\t\t// this is the first config with xkb_layout or xkb_file\n\t\t\tif (ic->identifier == input_config->identifier) {\n\t\t\t\ttranslate_keysyms(ic);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (int i = 0; i < config->input_type_configs->length; ++i) {\n\t\tstruct input_config *ic = config->input_type_configs->items[i];\n\t\tif (ic->xkb_layout || ic->xkb_file) {\n\t\t\t// this is the first config with xkb_layout or xkb_file\n\t\t\tif (ic->identifier == input_config->identifier) {\n\t\t\t\ttranslate_keysyms(ic);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic void input_manager_configure_input(\n\t\tstruct sway_input_device *input_device) {\n#if WLR_HAS_LIBINPUT_BACKEND\n\tbool config_changed = sway_input_configure_libinput_device(input_device);\n#else\n\tbool config_changed = false;\n#endif\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat_configure_device(seat, input_device);\n\t}\n\tif (config_changed) {\n\t\tipc_event_input(\"libinput_config\", input_device);\n\t}\n}\n\nvoid input_manager_configure_all_input_mappings(void) {\n\tstruct sway_input_device *input_device;\n\twl_list_for_each(input_device, &server.input->devices, link) {\n\t\tstruct sway_seat *seat;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tseat_configure_device_mapping(seat, input_device);\n\t\t}\n\n#if WLR_HAS_LIBINPUT_BACKEND\n\t\t// Input devices mapped to unavailable outputs get their libinput\n\t\t// send_events setting switched to false. We need to re-enable this\n\t\t// when the output appears.\n\t\tsway_input_configure_libinput_device_send_events(input_device);\n#endif\n\t}\n}\n\nvoid input_manager_apply_input_config(struct input_config *input_config) {\n\tstruct sway_input_device *input_device = NULL;\n\tbool wildcard = strcmp(input_config->identifier, \"*\") == 0;\n\tbool type_wildcard = has_prefix(input_config->identifier, \"type:\");\n\twl_list_for_each(input_device, &server.input->devices, link) {\n\t\tbool type_matches = type_wildcard &&\n\t\t\tstrcmp(input_device_get_type(input_device), input_config->identifier + 5) == 0;\n\t\tif (strcmp(input_device->identifier, input_config->identifier) == 0\n\t\t\t\t|| wildcard\n\t\t\t\t|| type_matches) {\n\t\t\tinput_manager_configure_input(input_device);\n\t\t}\n\t}\n\n\tretranslate_keysyms(input_config);\n}\n\nvoid input_manager_reset_input(struct sway_input_device *input_device) {\n#if WLR_HAS_LIBINPUT_BACKEND\n\tsway_input_reset_libinput_device(input_device);\n#endif\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat_reset_device(seat, input_device);\n\t}\n}\n\nvoid input_manager_reset_all_inputs(void) {\n\t// Set the active keyboard to NULL to avoid spamming configuration updates\n\t// for all keyboard devices.\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\twlr_seat_set_keyboard(seat->wlr_seat, NULL);\n\t}\n\n\tstruct sway_input_device *input_device = NULL;\n\twl_list_for_each(input_device, &server.input->devices, link) {\n\t\tinput_manager_reset_input(input_device);\n\t}\n\n\t// If there is at least one keyboard using the default keymap, repeat delay,\n\t// and repeat rate, then it is possible that there is a keyboard group that\n\t// need their keyboard disarmed.\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tstruct sway_keyboard_group *group;\n\t\twl_list_for_each(group, &seat->keyboard_groups, link) {\n\t\t\tsway_keyboard_disarm_key_repeat(group->seat_device->keyboard);\n\t\t}\n\t}\n}\n\nvoid input_manager_apply_seat_config(struct seat_config *seat_config) {\n\tsway_log(SWAY_DEBUG, \"applying seat config for seat %s\", seat_config->name);\n\tif (strcmp(seat_config->name, \"*\") == 0) {\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\t// Only apply the wildcard config directly if there is no seat\n\t\t\t// specific config\n\t\t\tstruct seat_config *sc = seat_get_config(seat);\n\t\t\tif (!sc) {\n\t\t\t\tsc = seat_config;\n\t\t\t}\n\t\t\tseat_apply_config(seat, sc);\n\t\t}\n\t} else {\n\t\tstruct sway_seat *seat =\n\t\t\tinput_manager_get_seat(seat_config->name, true);\n\t\tif (!seat) {\n\t\t\treturn;\n\t\t}\n\t\tseat_apply_config(seat, seat_config);\n\t}\n\n\t// for every device, try to add it to a seat and if no seat has it\n\t// attached, add it to the fallback seats.\n\tstruct sway_input_device *input_device = NULL;\n\twl_list_for_each(input_device, &server.input->devices, link) {\n\t\tlist_t *seat_list = create_list();\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tstruct seat_config *seat_config = seat_get_config(seat);\n\t\t\tif (!seat_config) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (seat_config_get_attachment(seat_config, \"*\") ||\n\t\t\t\t\tseat_config_get_attachment(seat_config,\n\t\t\t\t\t\tinput_device->identifier)) {\n\t\t\t\tlist_add(seat_list, seat);\n\t\t\t}\n\t\t}\n\n\t\tif (seat_list->length) {\n\t\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\t\tbool attached = false;\n\t\t\t\tfor (int i = 0; i < seat_list->length; ++i) {\n\t\t\t\t\tif (seat == seat_list->items[i]) {\n\t\t\t\t\t\tattached = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (attached) {\n\t\t\t\t\tseat_add_device(seat, input_device);\n\t\t\t\t} else {\n\t\t\t\t\tseat_remove_device(seat, input_device);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\t\tstruct seat_config *seat_config = seat_get_config(seat);\n\t\t\t\tif (seat_config && seat_config->fallback == 1) {\n\t\t\t\t\tseat_add_device(seat, input_device);\n\t\t\t\t} else {\n\t\t\t\t\tseat_remove_device(seat, input_device);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlist_free(seat_list);\n\t}\n}\n\nvoid input_manager_configure_xcursor(void) {\n\tstruct sway_seat *seat = NULL;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat_configure_xcursor(seat);\n\t}\n}\n\nstruct input_config *input_device_get_config(struct sway_input_device *device) {\n\tstruct input_config *wildcard_config = NULL;\n\tstruct input_config *input_config = NULL;\n\tfor (int i = 0; i < config->input_configs->length; ++i) {\n\t\tinput_config = config->input_configs->items[i];\n\t\tif (strcmp(input_config->identifier, device->identifier) == 0) {\n\t\t\treturn input_config;\n\t\t} else if (strcmp(input_config->identifier, \"*\") == 0) {\n\t\t\twildcard_config = input_config;\n\t\t}\n\t}\n\n\tconst char *device_type = input_device_get_type(device);\n\tfor (int i = 0; i < config->input_type_configs->length; ++i) {\n\t\tinput_config = config->input_type_configs->items[i];\n\t\tif (strcmp(input_config->identifier + 5, device_type) == 0) {\n\t\t\treturn input_config;\n\t\t}\n\t}\n\n\treturn wildcard_config;\n}\n"
  },
  {
    "path": "sway/input/keyboard.c",
    "content": "#include <assert.h>\n#include <limits.h>\n#include <strings.h>\n#include <wlr/config.h>\n#include <wlr/backend/multi.h>\n#include <wlr/interfaces/wlr_keyboard.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_keyboard.h>\n#include <wlr/types/wlr_keyboard_group.h>\n#include <xkbcommon/xkbcommon-names.h>\n#include \"sway/commands.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/server.h\"\n#include \"log.h\"\n\n#if WLR_HAS_SESSION\n#include <wlr/backend/session.h>\n#endif\n\nstatic struct modifier_key {\n\tchar *name;\n\tuint32_t mod;\n} modifiers[] = {\n\t{ XKB_MOD_NAME_SHIFT, WLR_MODIFIER_SHIFT },\n\t{ XKB_MOD_NAME_CAPS, WLR_MODIFIER_CAPS },\n\t{ XKB_MOD_NAME_CTRL, WLR_MODIFIER_CTRL },\n\t{ \"Ctrl\", WLR_MODIFIER_CTRL },\n\t{ XKB_MOD_NAME_ALT, WLR_MODIFIER_ALT },\n\t{ \"Alt\", WLR_MODIFIER_ALT },\n\t{ XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },\n\t{ \"Mod3\", WLR_MODIFIER_MOD3 },\n\t{ XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },\n\t{ \"Super\", WLR_MODIFIER_LOGO },\n\t{ \"Mod5\", WLR_MODIFIER_MOD5 },\n};\n\nuint32_t get_modifier_mask_by_name(const char *name) {\n\tint i;\n\tfor (i = 0; i < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++i) {\n\t\tif (strcasecmp(modifiers[i].name, name) == 0) {\n\t\t\treturn modifiers[i].mod;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nconst char *get_modifier_name_by_mask(uint32_t modifier) {\n\tint i;\n\tfor (i = 0; i < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++i) {\n\t\tif (modifiers[i].mod == modifier) {\n\t\t\treturn modifiers[i].name;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nint get_modifier_names(const char **names, uint32_t modifier_masks) {\n\tint length = 0;\n\tint i;\n\tfor (i = 0; i < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++i) {\n\t\tif ((modifier_masks & modifiers[i].mod) != 0) {\n\t\t\tnames[length] = modifiers[i].name;\n\t\t\t++length;\n\t\t\tmodifier_masks ^= modifiers[i].mod;\n\t\t}\n\t}\n\n\treturn length;\n}\n\n/**\n * Remove all key ids associated to a keycode from the list of pressed keys\n */\nstatic bool state_erase_key(struct sway_shortcut_state *state,\n\t\tuint32_t keycode) {\n\tbool found = false;\n\tsize_t j = 0;\n\tfor (size_t i = 0; i < state->npressed; ++i) {\n\t\tif (i > j) {\n\t\t\tstate->pressed_keys[j] = state->pressed_keys[i];\n\t\t\tstate->pressed_keycodes[j] = state->pressed_keycodes[i];\n\t\t}\n\t\tif (state->pressed_keycodes[i] != keycode) {\n\t\t\t++j;\n\t\t} else {\n\t\t\tfound = true;\n\t\t}\n\t}\n\twhile(state->npressed > j) {\n\t\t--state->npressed;\n\t\tstate->pressed_keys[state->npressed] = 0;\n\t\tstate->pressed_keycodes[state->npressed] = 0;\n\t}\n\tstate->current_key = 0;\n\treturn found;\n}\n\n/**\n * Add a key id (with associated keycode) to the list of pressed keys,\n * if the list is not full.\n */\nstatic void state_add_key(struct sway_shortcut_state *state,\n\t\tuint32_t keycode, uint32_t key_id) {\n\tif (state->npressed >= SWAY_KEYBOARD_PRESSED_KEYS_CAP) {\n\t\treturn;\n\t}\n\tsize_t i = 0;\n\twhile (i < state->npressed && state->pressed_keys[i] < key_id) {\n\t\t++i;\n\t}\n\tsize_t j = state->npressed;\n\twhile (j > i) {\n\t\tstate->pressed_keys[j] = state->pressed_keys[j - 1];\n\t\tstate->pressed_keycodes[j] = state->pressed_keycodes[j - 1];\n\t\t--j;\n\t}\n\tstate->pressed_keys[i] = key_id;\n\tstate->pressed_keycodes[i] = keycode;\n\tstate->npressed++;\n\tstate->current_key = key_id;\n}\n\n/**\n * Update the shortcut model state in response to new input\n */\nstatic bool update_shortcut_state(struct sway_shortcut_state *state,\n\t\tuint32_t keycode, enum wl_keyboard_key_state keystate, uint32_t new_key,\n\t\tuint32_t raw_modifiers) {\n\tbool last_key_was_a_modifier = raw_modifiers != state->last_raw_modifiers;\n\tstate->last_raw_modifiers = raw_modifiers;\n\n\tif (last_key_was_a_modifier && state->last_keycode) {\n\t\t// Last pressed key before this one was a modifier\n\t\tstate_erase_key(state, state->last_keycode);\n\t}\n\n\tif (keystate == WL_KEYBOARD_KEY_STATE_PRESSED) {\n\t\t// Add current key to set; there may be duplicates\n\t\tstate_add_key(state, keycode, new_key);\n\t\tstate->last_keycode = keycode;\n\t} else {\n\t\treturn state_erase_key(state, keycode);\n\t}\n\n\treturn false;\n}\n\n/**\n * If one exists, finds a binding which matches the shortcut model state,\n * current modifiers, release state, and locked state.\n */\nstatic void get_active_binding(const struct sway_shortcut_state *state,\n\t\tlist_t *bindings, struct sway_binding **current_binding,\n\t\tuint32_t modifiers, bool release, bool locked, bool inhibited,\n\t\tconst char *input, bool exact_input, xkb_layout_index_t group) {\n\tfor (int i = 0; i < bindings->length; ++i) {\n\t\tstruct sway_binding *binding = bindings->items[i];\n\t\tbool binding_locked = (binding->flags & BINDING_LOCKED) != 0;\n\t\tbool binding_inhibited = (binding->flags & BINDING_INHIBITED) != 0;\n\t\tbool binding_release = binding->flags & BINDING_RELEASE;\n\n\t\tif (modifiers ^ binding->modifiers ||\n\t\t\t\trelease != binding_release ||\n\t\t\t\tlocked > binding_locked ||\n\t\t\t\tinhibited > binding_inhibited ||\n\t\t\t\t(binding->group != XKB_LAYOUT_INVALID &&\n\t\t\t\t binding->group != group) ||\n\t\t\t\t(strcmp(binding->input, input) != 0 &&\n\t\t\t\t (strcmp(binding->input, \"*\") != 0 || exact_input))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool match = false;\n\t\tif (state->npressed == (size_t)binding->keys->length) {\n\t\t\tmatch = true;\n\t\t\tfor (size_t j = 0; j < state->npressed; j++) {\n\t\t\t\tuint32_t key = *(uint32_t *)binding->keys->items[j];\n\t\t\t\tif (key != state->pressed_keys[j]) {\n\t\t\t\t\tmatch = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (binding->keys->length == 1) {\n\t\t\t/*\n\t\t\t * If no multiple-key binding has matched, try looking for\n\t\t\t * single-key bindings that match the newly-pressed key.\n\t\t\t */\n\t\t\tmatch = state->current_key == *(uint32_t *)binding->keys->items[0];\n\t\t}\n\t\tif (!match) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (*current_binding) {\n\t\t\tif (*current_binding == binding) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbool current_locked =\n\t\t\t\t((*current_binding)->flags & BINDING_LOCKED) != 0;\n\t\t\tbool current_inhibited =\n\t\t\t\t((*current_binding)->flags & BINDING_INHIBITED) != 0;\n\t\t\tbool current_input = strcmp((*current_binding)->input, input) == 0;\n\t\t\tbool current_group_set =\n\t\t\t\t(*current_binding)->group != XKB_LAYOUT_INVALID;\n\t\t\tbool binding_input = strcmp(binding->input, input) == 0;\n\t\t\tbool binding_group_set = binding->group != XKB_LAYOUT_INVALID;\n\n\t\t\tif (current_input == binding_input\n\t\t\t\t\t&& current_locked == binding_locked\n\t\t\t\t\t&& current_inhibited == binding_inhibited\n\t\t\t\t\t&& current_group_set == binding_group_set) {\n\t\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\t\"Encountered conflicting bindings %d and %d\",\n\t\t\t\t\t\t(*current_binding)->order, binding->order);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (current_input && !binding_input) {\n\t\t\t\tcontinue; // Prefer the correct input\n\t\t\t}\n\n\t\t\tif (current_input == binding_input &&\n\t\t\t\t   (*current_binding)->group == group) {\n\t\t\t\tcontinue; // Prefer correct group for matching inputs\n\t\t\t}\n\n\t\t\tif (current_input == binding_input &&\n\t\t\t\t\tcurrent_group_set == binding_group_set &&\n\t\t\t\t\tcurrent_locked == locked) {\n\t\t\t\tcontinue; // Prefer correct lock state for matching input+group\n\t\t\t}\n\n\t\t\tif (current_input == binding_input &&\n\t\t\t\t\tcurrent_group_set == binding_group_set &&\n\t\t\t\t\tcurrent_locked == binding_locked &&\n\t\t\t\t\tcurrent_inhibited == inhibited) {\n\t\t\t\t// Prefer correct inhibition state for matching\n\t\t\t\t// input+group+locked\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t*current_binding = binding;\n\t\tif (strcmp((*current_binding)->input, input) == 0 &&\n\t\t\t\t(((*current_binding)->flags & BINDING_LOCKED) == locked) &&\n\t\t\t\t(((*current_binding)->flags & BINDING_INHIBITED) == inhibited) &&\n\t\t\t\t(*current_binding)->group == group) {\n\t\t\treturn; // If a perfect match is found, quit searching\n\t\t}\n\t}\n}\n\n/**\n * Execute a built-in, hardcoded compositor binding. These are triggered from a\n * single keysym.\n *\n * Returns true if the keysym was handled by a binding and false if the event\n * should be propagated to clients.\n */\nstatic bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,\n\t\tconst xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) {\n\tfor (size_t i = 0; i < keysyms_len; ++i) {\n\t\txkb_keysym_t keysym = pressed_keysyms[i];\n\n\t\tif (keysym >= XKB_KEY_XF86Switch_VT_1 &&\n\t\t\t\tkeysym <= XKB_KEY_XF86Switch_VT_12) {\n#if WLR_HAS_SESSION\n\t\t\tif (server.session) {\n\t\t\t\tunsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;\n\t\t\t\twlr_session_change_vt(server.session, vt);\n\t\t\t}\n#endif\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic bool keyboard_execute_pointer_keysyms(struct sway_keyboard *keyboard,\n\t\tuint32_t time, const xkb_keysym_t *pressed_keysyms, size_t keysyms_len,\n\t\tenum wl_keyboard_key_state state) {\n\tstruct sway_cursor *cursor = keyboard->seat_device->sway_seat->cursor;\n\n\tfor (size_t i = 0; i < keysyms_len; ++i) {\n\t\txkb_keysym_t keysym = pressed_keysyms[i];\n\n\t\tuint32_t button = wlr_keyboard_keysym_to_pointer_button(keysym);\n\t\tif (button != 0) {\n\t\t\tdispatch_cursor_button(cursor, &keyboard->wlr->base, time, button,\n\t\t\t\t(enum wl_pointer_button_state)state);\n\t\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t\t\treturn true;\n\t\t}\n\n\t\tint dx, dy;\n\t\twlr_keyboard_keysym_to_pointer_motion(keysym, &dx, &dy);\n\t\tif (state == WL_KEYBOARD_KEY_STATE_PRESSED && (dx != 0 || dy != 0)) {\n\t\t\tdx *= 10;\n\t\t\tdy *= 10;\n\t\t\tpointer_motion(cursor, time, &keyboard->wlr->base, dx, dy, dx, dy);\n\t\t\twlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Get keysyms and modifiers from the keyboard as xkb sees them.\n *\n * This uses the xkb keysyms translation based on pressed modifiers and clears\n * the consumed modifiers from the list of modifiers passed to keybind\n * detection.\n *\n * On US layout, pressing Alt+Shift+2 will trigger Alt+@.\n */\nstatic size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,\n\t\txkb_keycode_t keycode, const xkb_keysym_t **keysyms,\n\t\tuint32_t *modifiers) {\n\t*modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);\n\txkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(\n\t\tkeyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);\n\t*modifiers = *modifiers & ~consumed;\n\n\treturn xkb_state_key_get_syms(keyboard->wlr->xkb_state,\n\t\tkeycode, keysyms);\n}\n\n/**\n * Get keysyms and modifiers from the keyboard as if modifiers didn't change\n * keysyms.\n *\n * This avoids the xkb keysym translation based on modifiers considered pressed\n * in the state.\n *\n * This will trigger keybinds such as Alt+Shift+2.\n */\nstatic size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,\n\t\txkb_keycode_t keycode, const xkb_keysym_t **keysyms,\n\t\tuint32_t *modifiers) {\n\t*modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);\n\n\txkb_layout_index_t layout_index = xkb_state_key_get_layout(\n\t\tkeyboard->wlr->xkb_state, keycode);\n\treturn xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap,\n\t\tkeycode, layout_index, 0, keysyms);\n}\n\nvoid sway_keyboard_disarm_key_repeat(struct sway_keyboard *keyboard) {\n\tif (!keyboard) {\n\t\treturn;\n\t}\n\tkeyboard->repeat_binding = NULL;\n\tif (wl_event_source_timer_update(keyboard->key_repeat_source, 0) < 0) {\n\t\tsway_log(SWAY_DEBUG, \"failed to disarm key repeat timer\");\n\t}\n}\n\nstruct key_info {\n\tuint32_t keycode;\n\tuint32_t code_modifiers;\n\n\tconst xkb_keysym_t *raw_keysyms;\n\tuint32_t raw_modifiers;\n\tsize_t raw_keysyms_len;\n\n\tconst xkb_keysym_t *translated_keysyms;\n\tuint32_t translated_modifiers;\n\tsize_t translated_keysyms_len;\n};\n\nstatic void update_keyboard_state(struct sway_keyboard *keyboard,\n\t\tuint32_t raw_keycode, enum wl_keyboard_key_state keystate,\n\t\tstruct key_info *keyinfo) {\n\t// Identify new keycode, raw keysym(s), and translated keysym(s)\n\tkeyinfo->keycode = raw_keycode + 8;\n\n\tkeyinfo->raw_keysyms_len = keyboard_keysyms_raw(keyboard, keyinfo->keycode,\n\t\t\t&keyinfo->raw_keysyms, &keyinfo->raw_modifiers);\n\n\tkeyinfo->translated_keysyms_len = keyboard_keysyms_translated(keyboard,\n\t\t\tkeyinfo->keycode, &keyinfo->translated_keysyms,\n\t\t\t&keyinfo->translated_modifiers);\n\n\tkeyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);\n\n\t// Update shortcut model keyinfo\n\tupdate_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate,\n\t\t\tkeyinfo->keycode, keyinfo->code_modifiers);\n\tfor (size_t i = 0; i < keyinfo->raw_keysyms_len; ++i) {\n\t\tupdate_shortcut_state(&keyboard->state_keysyms_raw,\n\t\t\t\traw_keycode, keystate, keyinfo->raw_keysyms[i],\n\t\t\t\tkeyinfo->code_modifiers);\n\t}\n\tfor (size_t i = 0; i < keyinfo->translated_keysyms_len; ++i) {\n\t\tupdate_shortcut_state(&keyboard->state_keysyms_translated,\n\t\t\t\traw_keycode, keystate, keyinfo->translated_keysyms[i],\n\t\t\t\tkeyinfo->code_modifiers);\n\t}\n}\n\n/**\n * Get keyboard grab of the seat from sway_keyboard if we should forward events\n * to it.\n *\n * Returns NULL if the keyboard is not grabbed by an input method,\n * or if event is from virtual keyboard of the same client as grab.\n * TODO: see swaywm/wlroots#2322\n */\nstatic struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(\n\t\tstruct sway_keyboard *keyboard) {\n\tstruct wlr_input_method_v2 *input_method = keyboard->seat_device->\n\t\tsway_seat->im_relay.input_method;\n\tstruct wlr_virtual_keyboard_v1 *virtual_keyboard =\n\t\twlr_input_device_get_virtual_keyboard(keyboard->seat_device->input_device->wlr_device);\n\tif (!input_method || !input_method->keyboard_grab || (virtual_keyboard &&\n\t\t\t\twl_resource_get_client(virtual_keyboard->resource) ==\n\t\t\t\twl_resource_get_client(input_method->keyboard_grab->resource))) {\n\t\treturn NULL;\n\t}\n\treturn input_method->keyboard_grab;\n}\n\nstatic void handle_key_event(struct sway_keyboard *keyboard,\n\t\tstruct wlr_keyboard_key_event *event) {\n\tstruct sway_seat *seat = keyboard->seat_device->sway_seat;\n\tstruct wlr_seat *wlr_seat = seat->wlr_seat;\n\tstruct wlr_input_device *wlr_device =\n\t\tkeyboard->seat_device->input_device->wlr_device;\n\tchar *device_identifier = input_device_get_identifier(wlr_device);\n\tbool exact_identifier = keyboard->wlr->group != NULL;\n\tseat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);\n\tbool locked = server.session_lock.lock;\n\tstruct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =\n\t\tkeyboard_shortcuts_inhibitor_get_for_focused_surface(seat);\n\tbool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;\n\n\tif (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {\n\t\tcursor_notify_key_press(seat->cursor);\n\t}\n\n\t// Identify new keycode, raw keysym(s), and translated keysym(s)\n\tstruct key_info keyinfo;\n\tupdate_keyboard_state(keyboard, event->keycode, event->state, &keyinfo);\n\n\tbool handled = false;\n\t// Identify active release binding\n\tstruct sway_binding *binding_released = NULL;\n\tget_active_binding(&keyboard->state_keycodes,\n\t\t\tconfig->current_mode->keycode_bindings, &binding_released,\n\t\t\tkeyinfo.code_modifiers, true, locked,\n\t\t\tshortcuts_inhibited, device_identifier,\n\t\t\texact_identifier, keyboard->effective_layout);\n\tget_active_binding(&keyboard->state_keysyms_raw,\n\t\t\tconfig->current_mode->keysym_bindings, &binding_released,\n\t\t\tkeyinfo.raw_modifiers, true, locked,\n\t\t\tshortcuts_inhibited, device_identifier,\n\t\t\texact_identifier, keyboard->effective_layout);\n\tget_active_binding(&keyboard->state_keysyms_translated,\n\t\t\tconfig->current_mode->keysym_bindings, &binding_released,\n\t\t\tkeyinfo.translated_modifiers, true, locked,\n\t\t\tshortcuts_inhibited, device_identifier,\n\t\t\texact_identifier, keyboard->effective_layout);\n\n\t// Execute stored release binding once no longer active\n\tif (keyboard->held_binding && binding_released != keyboard->held_binding &&\n\t\t\tevent->state == WL_KEYBOARD_KEY_STATE_RELEASED) {\n\t\tseat_execute_command(seat, keyboard->held_binding);\n\t\thandled = true;\n\t}\n\tif (binding_released != keyboard->held_binding) {\n\t\tkeyboard->held_binding = NULL;\n\t}\n\tif (binding_released && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {\n\t\tkeyboard->held_binding = binding_released;\n\t}\n\n\t// Identify and execute active pressed binding\n\tstruct sway_binding *binding = NULL;\n\tif (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {\n\t\tget_active_binding(&keyboard->state_keycodes,\n\t\t\t\tconfig->current_mode->keycode_bindings, &binding,\n\t\t\t\tkeyinfo.code_modifiers, false, locked,\n\t\t\t\tshortcuts_inhibited, device_identifier,\n\t\t\t\texact_identifier, keyboard->effective_layout);\n\t\tget_active_binding(&keyboard->state_keysyms_raw,\n\t\t\t\tconfig->current_mode->keysym_bindings, &binding,\n\t\t\t\tkeyinfo.raw_modifiers, false, locked,\n\t\t\t\tshortcuts_inhibited, device_identifier,\n\t\t\t\texact_identifier, keyboard->effective_layout);\n\t\tget_active_binding(&keyboard->state_keysyms_translated,\n\t\t\t\tconfig->current_mode->keysym_bindings, &binding,\n\t\t\t\tkeyinfo.translated_modifiers, false, locked,\n\t\t\t\tshortcuts_inhibited, device_identifier,\n\t\t\t\texact_identifier, keyboard->effective_layout);\n\t}\n\n\t// Set up (or clear) keyboard repeat for a pressed binding. Since the\n\t// binding may remove the keyboard, the timer needs to be updated first\n\tif (binding && !(binding->flags & BINDING_NOREPEAT) &&\n\t\t\tkeyboard->wlr->repeat_info.delay > 0) {\n\t\tkeyboard->repeat_binding = binding;\n\t\tif (wl_event_source_timer_update(keyboard->key_repeat_source,\n\t\t\t\tkeyboard->wlr->repeat_info.delay) < 0) {\n\t\t\tsway_log(SWAY_DEBUG, \"failed to set key repeat timer\");\n\t\t}\n\t} else if (keyboard->repeat_binding) {\n\t\tsway_keyboard_disarm_key_repeat(keyboard);\n\t}\n\n\tif (binding) {\n\t\tseat_execute_command(seat, binding);\n\t\thandled = true;\n\t}\n\n\tif (!handled && keyboard->wlr->group) {\n\t\t// Only handle device specific bindings for keyboards in a group\n\t\tfree(device_identifier);\n\t\treturn;\n\t}\n\n\t// Compositor bindings\n\tif (!handled && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {\n\t\thandled = keyboard_execute_compositor_binding(\n\t\t\t\tkeyboard, keyinfo.translated_keysyms,\n\t\t\t\tkeyinfo.translated_modifiers, keyinfo.translated_keysyms_len);\n\t}\n\tif (!handled && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {\n\t\thandled = keyboard_execute_compositor_binding(\n\t\t\t\tkeyboard, keyinfo.raw_keysyms, keyinfo.raw_modifiers,\n\t\t\t\tkeyinfo.raw_keysyms_len);\n\t}\n\tif (!handled) {\n\t\thandled = keyboard_execute_pointer_keysyms(keyboard, event->time_msec,\n\t\t\tkeyinfo.translated_keysyms, keyinfo.translated_keysyms_len,\n\t\t\tevent->state);\n\t}\n\n\tif (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {\n\t\t// If the pressed event was sent to a client and we have a focused\n\t\t// surface immediately before this event, also send the released\n\t\t// event. In particular, don't send the released event to the IM grab.\n\t\tbool pressed_sent = update_shortcut_state(\n\t\t\t&keyboard->state_pressed_sent, event->keycode,\n\t\t\tevent->state, keyinfo.keycode, 0);\n\t\tif (pressed_sent && seat->wlr_seat->keyboard_state.focused_surface) {\n\t\t\twlr_seat_set_keyboard(wlr_seat, keyboard->wlr);\n\t\t\twlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,\n\t\t\t\tevent->keycode, event->state);\n\t\t\thandled = true;\n\t\t}\n\t}\n\n\tif (!handled) {\n\t\tstruct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);\n\n\t\tif (kb_grab) {\n\t\t\twlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);\n\t\t\twlr_input_method_keyboard_grab_v2_send_key(kb_grab,\n\t\t\t\tevent->time_msec, event->keycode, event->state);\n\t\t\thandled = true;\n\t\t}\n\t}\n\n\tif (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) {\n\t\t// If a released event failed pressed sent test, and not in sent to\n\t\t// keyboard grab, it is still not handled. Don't handle released here.\n\t\tupdate_shortcut_state(\n\t\t\t&keyboard->state_pressed_sent, event->keycode, event->state,\n\t\t\tkeyinfo.keycode, 0);\n\t\twlr_seat_set_keyboard(wlr_seat, keyboard->wlr);\n\t\twlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,\n\t\t\t\tevent->keycode, event->state);\n\t}\n\n\tfree(device_identifier);\n}\n\nstatic void handle_keyboard_key(struct wl_listener *listener, void *data) {\n\tstruct sway_keyboard *keyboard =\n\t\twl_container_of(listener, keyboard, keyboard_key);\n\thandle_key_event(keyboard, data);\n}\n\nstatic void handle_keyboard_group_key(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_keyboard_group *sway_group =\n\t\twl_container_of(listener, sway_group, keyboard_key);\n\thandle_key_event(sway_group->seat_device->keyboard, data);\n}\n\nstatic void handle_keyboard_group_enter(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_keyboard_group *sway_group =\n\t\twl_container_of(listener, sway_group, enter);\n\tstruct sway_keyboard *keyboard = sway_group->seat_device->keyboard;\n\tstruct wl_array *keycodes = data;\n\n\tuint32_t *keycode;\n\twl_array_for_each(keycode, keycodes) {\n\t\tstruct key_info keyinfo;\n\t\tupdate_keyboard_state(keyboard, *keycode, WL_KEYBOARD_KEY_STATE_PRESSED, &keyinfo);\n\t}\n}\n\nstatic void handle_keyboard_group_leave(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_keyboard_group *sway_group =\n\t\twl_container_of(listener, sway_group, leave);\n\tstruct sway_keyboard *keyboard = sway_group->seat_device->keyboard;\n\tstruct wl_array *keycodes = data;\n\n\tbool pressed_sent = false;\n\n\tuint32_t *keycode;\n\twl_array_for_each(keycode, keycodes) {\n\t\tstruct key_info keyinfo;\n\t\tupdate_keyboard_state(keyboard, *keycode, WL_KEYBOARD_KEY_STATE_RELEASED, &keyinfo);\n\n\t\tpressed_sent |= update_shortcut_state(&keyboard->state_pressed_sent,\n\t\t\t\t*keycode, WL_KEYBOARD_KEY_STATE_RELEASED, keyinfo.keycode, 0);\n\t}\n\n\tif (!pressed_sent) {\n\t\treturn;\n\t}\n\n\t// Refocus the focused node, layer surface, or unmanaged surface so that\n\t// it picks up the current keyboard state.\n\tstruct sway_seat *seat = keyboard->seat_device->sway_seat;\n\tstruct sway_node *focus = seat_get_focus(seat);\n\tif (focus) {\n\t\tseat_set_focus(seat, NULL);\n\t\tseat_set_focus(seat, focus);\n\t} else if (seat->focused_layer) {\n\t\tstruct wlr_layer_surface_v1 *layer = seat->focused_layer;\n\t\tseat_set_focus_layer(seat, NULL);\n\t\tseat_set_focus_layer(seat, layer);\n\t} else {\n\t\tstruct wlr_surface *unmanaged = seat->wlr_seat->keyboard_state.focused_surface;\n\t\tseat_set_focus_surface(seat, NULL, false);\n\t\tseat_set_focus_surface(seat, unmanaged, false);\n\t}\n}\n\nstatic int handle_keyboard_repeat(void *data) {\n\tstruct sway_keyboard *keyboard = data;\n\tif (keyboard->repeat_binding) {\n\t\tif (keyboard->wlr->repeat_info.rate > 0) {\n\t\t\t// We queue the next event first, as the command might cancel it\n\t\t\tif (wl_event_source_timer_update(keyboard->key_repeat_source,\n\t\t\t\t\t1000 / keyboard->wlr->repeat_info.rate) < 0) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"failed to update key repeat timer\");\n\t\t\t}\n\t\t}\n\n\t\tseat_execute_command(keyboard->seat_device->sway_seat,\n\t\t\t\tkeyboard->repeat_binding);\n\t}\n\treturn 0;\n}\n\nstatic void determine_bar_visibility(uint32_t modifiers) {\n\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\tstruct bar_config *bar = config->bars->items[i];\n\t\tif (bar->modifier == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool vis_by_mod = (~modifiers & bar->modifier) == 0;\n\t\tif (bar->visible_by_modifier != vis_by_mod) {\n\t\t\t// If visible by modifier is set, send that it is no longer visible\n\t\t\t// by modifier (regardless of bar mode and state). Otherwise, only\n\t\t\t// send the visible by modifier status if mode and state are hide\n\t\t\tif (bar->visible_by_modifier\n\t\t\t\t\t|| strcmp(bar->mode, bar->hidden_state) == 0) {\n\t\t\t\tbar->visible_by_modifier = vis_by_mod;\n\t\t\t\tipc_event_bar_state_update(bar);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void handle_modifier_event(struct sway_keyboard *keyboard) {\n\tif (!keyboard->wlr->group) {\n\t\tstruct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);\n\n\t\tif (kb_grab) {\n\t\t\twlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);\n\t\t\twlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,\n\t\t\t\t\t&keyboard->wlr->modifiers);\n\t\t} else {\n\t\t\tstruct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;\n\t\t\twlr_seat_set_keyboard(wlr_seat, keyboard->wlr);\n\t\t\twlr_seat_keyboard_notify_modifiers(wlr_seat,\n\t\t\t\t\t&keyboard->wlr->modifiers);\n\t\t}\n\n\t\tuint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);\n\t\tdetermine_bar_visibility(modifiers);\n\t}\n\n\tif (keyboard->wlr->modifiers.group != keyboard->effective_layout) {\n\t\tkeyboard->effective_layout = keyboard->wlr->modifiers.group;\n\n\t\tif (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) {\n\t\t\tipc_event_input(\"xkb_layout\", keyboard->seat_device->input_device);\n\t\t}\n\t}\n}\n\nstatic void handle_keyboard_modifiers(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_keyboard *keyboard =\n\t\twl_container_of(listener, keyboard, keyboard_modifiers);\n\thandle_modifier_event(keyboard);\n}\n\nstatic void handle_keyboard_group_modifiers(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_keyboard_group *group =\n\t\twl_container_of(listener, group, keyboard_modifiers);\n\thandle_modifier_event(group->seat_device->keyboard);\n}\n\nstruct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device) {\n\tstruct sway_keyboard *keyboard =\n\t\tcalloc(1, sizeof(struct sway_keyboard));\n\tif (!sway_assert(keyboard, \"could not allocate sway keyboard\")) {\n\t\treturn NULL;\n\t}\n\n\tkeyboard->seat_device = device;\n\tkeyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device);\n\tdevice->keyboard = keyboard;\n\n\twl_list_init(&keyboard->keyboard_key.link);\n\twl_list_init(&keyboard->keyboard_modifiers.link);\n\n\tkeyboard->key_repeat_source = wl_event_loop_add_timer(server.wl_event_loop,\n\t\t\thandle_keyboard_repeat, keyboard);\n\n\treturn keyboard;\n}\n\nstatic void handle_xkb_context_log(struct xkb_context *context,\n\t\tenum xkb_log_level level, const char *format, va_list args) {\n\tchar *error = vformat_str(format, args);\n\n\tsize_t len = strlen(error);\n\tif (error[len - 1] == '\\n') {\n\t\terror[len - 1] = '\\0';\n\t}\n\n\tsway_log_importance_t importance = SWAY_DEBUG;\n\tif (level <= XKB_LOG_LEVEL_ERROR) { // Critical and Error\n\t\timportance = SWAY_ERROR;\n\t} else if (level <= XKB_LOG_LEVEL_INFO) { // Warning and Info\n\t\timportance = SWAY_INFO;\n\t}\n\tsway_log(importance, \"[xkbcommon] %s\", error);\n\n\tchar **data = xkb_context_get_user_data(context);\n\tif (importance == SWAY_ERROR && data && !*data) {\n\t\t*data = error;\n\t} else {\n\t\tfree(error);\n\t}\n}\n\nstruct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,\n\t\tchar **error) {\n\tstruct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV);\n\tif (!sway_assert(context, \"cannot create XKB context\")) {\n\t\treturn NULL;\n\t}\n\txkb_context_set_user_data(context, error);\n\txkb_context_set_log_fn(context, handle_xkb_context_log);\n\n\tstruct xkb_keymap *keymap = NULL;\n\n\tif (ic && ic->xkb_file) {\n\t\tFILE *keymap_file = fopen(ic->xkb_file, \"r\");\n\t\tif (!keymap_file) {\n\t\t\tsway_log_errno(SWAY_ERROR, \"cannot read xkb file %s\", ic->xkb_file);\n\t\t\tif (error) {\n\t\t\t\t*error = format_str(\"cannot read xkb file %s: %s\",\n\t\t\t\t\tic->xkb_file, strerror(errno));\n\t\t\t}\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\tkeymap = xkb_keymap_new_from_file(context, keymap_file,\n\t\t\t\t\tXKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);\n\n\t\tif (fclose(keymap_file) != 0) {\n\t\t\tsway_log_errno(SWAY_ERROR, \"Failed to close xkb file %s\",\n\t\t\t\t\tic->xkb_file);\n\t\t}\n\t} else {\n\t\tstruct xkb_rule_names rules = {0};\n\t\tif (ic) {\n\t\t\tinput_config_fill_rule_names(ic, &rules);\n\t\t}\n\t\tkeymap = xkb_keymap_new_from_names(context, &rules,\n\t\t\tXKB_KEYMAP_COMPILE_NO_FLAGS);\n\t}\n\ncleanup:\n\txkb_context_set_user_data(context, NULL);\n\txkb_context_unref(context);\n\treturn keymap;\n}\n\nstatic bool repeat_info_match(struct sway_keyboard *a, struct wlr_keyboard *b) {\n\treturn a->repeat_rate == b->repeat_info.rate &&\n\t\ta->repeat_delay == b->repeat_info.delay;\n}\n\nstatic void destroy_empty_wlr_keyboard_group(void *data) {\n\twlr_keyboard_group_destroy(data);\n}\n\nstatic void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {\n\tstruct sway_input_device *device = keyboard->seat_device->input_device;\n\tstruct wlr_keyboard_group *wlr_group = keyboard->wlr->group;\n\n\tsway_log(SWAY_DEBUG, \"Removing keyboard %s from group %p\",\n\t\t\tdevice->identifier, wlr_group);\n\n\twlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr);\n\n\tif (wl_list_empty(&wlr_group->devices)) {\n\t\tsway_log(SWAY_DEBUG, \"Destroying empty keyboard group %p\",\n\t\t\t\twlr_group);\n\t\tstruct sway_keyboard_group *sway_group = wlr_group->data;\n\t\twlr_group->data = NULL;\n\t\twl_list_remove(&sway_group->link);\n\t\twl_list_remove(&sway_group->keyboard_key.link);\n\t\twl_list_remove(&sway_group->keyboard_modifiers.link);\n\t\twl_list_remove(&sway_group->enter.link);\n\t\twl_list_remove(&sway_group->leave.link);\n\t\tsway_keyboard_destroy(sway_group->seat_device->keyboard);\n\t\tfree(sway_group->seat_device->input_device);\n\t\tfree(sway_group->seat_device);\n\t\tfree(sway_group);\n\n\t\t// To prevent use-after-free conditions when handling key events, defer\n\t\t// freeing the wlr_keyboard_group until idle\n\t\twl_event_loop_add_idle(server.wl_event_loop,\n\t\t\t\tdestroy_empty_wlr_keyboard_group, wlr_group);\n\t}\n}\n\nstatic void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {\n\tif (!keyboard->wlr->group) {\n\t\treturn;\n\t}\n\n\tstruct sway_seat *seat = keyboard->seat_device->sway_seat;\n\tstruct seat_config *sc = seat_get_config(seat);\n\tif (!sc) {\n\t\tsc = seat_get_config_by_name(\"*\");\n\t}\n\n\tswitch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) {\n\tcase KEYBOARD_GROUP_NONE:\n\t\tsway_keyboard_group_remove(keyboard);\n\t\tbreak;\n\tcase KEYBOARD_GROUP_DEFAULT: /* fallthrough */\n\tcase KEYBOARD_GROUP_SMART:;\n\t\tstruct wlr_keyboard_group *group = keyboard->wlr->group;\n\t\tif (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) ||\n\t\t\t\t!repeat_info_match(keyboard, &group->keyboard)) {\n\t\t\tsway_keyboard_group_remove(keyboard);\n\t\t}\n\t\tbreak;\n\t}\n}\n\nstatic void sway_keyboard_group_add(struct sway_keyboard *keyboard) {\n\tstruct sway_input_device *device = keyboard->seat_device->input_device;\n\tstruct sway_seat *seat = keyboard->seat_device->sway_seat;\n\tstruct seat_config *sc = seat_get_config(seat);\n\n\tif (device->is_virtual) {\n\t\t// Virtual devices should not be grouped\n\t\treturn;\n\t}\n\n\tif (!sc) {\n\t\tsc = seat_get_config_by_name(\"*\");\n\t}\n\n\tif (sc && sc->keyboard_grouping == KEYBOARD_GROUP_NONE) {\n\t\t// Keyboard grouping is disabled for the seat\n\t\treturn;\n\t}\n\n\tstruct sway_keyboard_group *group;\n\twl_list_for_each(group, &seat->keyboard_groups, link) {\n\t\tswitch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) {\n\t\tcase KEYBOARD_GROUP_NONE:\n\t\t\t// Nothing to do. This shouldn't even be reached\n\t\t\treturn;\n\t\tcase KEYBOARD_GROUP_DEFAULT: /* fallthrough */\n\t\tcase KEYBOARD_GROUP_SMART:;\n\t\t\tstruct wlr_keyboard_group *wlr_group = group->wlr_group;\n\t\t\tif (wlr_keyboard_keymaps_match(keyboard->keymap,\n\t\t\t\t\t\twlr_group->keyboard.keymap) &&\n\t\t\t\t\trepeat_info_match(keyboard, &wlr_group->keyboard)) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Adding keyboard %s to group %p\",\n\t\t\t\t\t\tdevice->identifier, wlr_group);\n\t\t\t\twlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tstruct sway_keyboard_group *sway_group =\n\t\tcalloc(1, sizeof(struct sway_keyboard_group));\n\tif (!sway_group) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate sway_keyboard_group\");\n\t\treturn;\n\t}\n\n\tsway_group->wlr_group = wlr_keyboard_group_create();\n\tif (!sway_group->wlr_group) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create keyboard group\");\n\t\tgoto cleanup;\n\t}\n\tsway_group->wlr_group->data = sway_group;\n\twlr_keyboard_set_keymap(&sway_group->wlr_group->keyboard, keyboard->keymap);\n\twlr_keyboard_set_repeat_info(&sway_group->wlr_group->keyboard,\n\t\t\tkeyboard->repeat_rate, keyboard->repeat_delay);\n\tsway_log(SWAY_DEBUG, \"Created keyboard group %p\", sway_group->wlr_group);\n\n\tsway_group->seat_device = calloc(1, sizeof(struct sway_seat_device));\n\tif (!sway_group->seat_device) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate sway_seat_device for group\");\n\t\tgoto cleanup;\n\t}\n\tsway_group->seat_device->sway_seat = seat;\n\n\tsway_group->seat_device->input_device =\n\t\tcalloc(1, sizeof(struct sway_input_device));\n\tif (!sway_group->seat_device->input_device) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate sway_input_device for group\");\n\t\tgoto cleanup;\n\t}\n\tsway_group->seat_device->input_device->wlr_device =\n\t\t&sway_group->wlr_group->keyboard.base;\n\n\tif (!sway_keyboard_create(seat, sway_group->seat_device)) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate sway_keyboard for group\");\n\t\tgoto cleanup;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Adding keyboard %s to group %p\",\n\t\t\tdevice->identifier, sway_group->wlr_group);\n\twlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr);\n\n\twl_list_insert(&seat->keyboard_groups, &sway_group->link);\n\n\twl_signal_add(&sway_group->wlr_group->keyboard.events.key,\n\t\t\t&sway_group->keyboard_key);\n\tsway_group->keyboard_key.notify = handle_keyboard_group_key;\n\n\twl_signal_add(&sway_group->wlr_group->keyboard.events.modifiers,\n\t\t\t&sway_group->keyboard_modifiers);\n\tsway_group->keyboard_modifiers.notify = handle_keyboard_group_modifiers;\n\n\twl_signal_add(&sway_group->wlr_group->events.enter, &sway_group->enter);\n\tsway_group->enter.notify = handle_keyboard_group_enter;\n\n\twl_signal_add(&sway_group->wlr_group->events.leave, &sway_group->leave);\n\tsway_group->leave.notify = handle_keyboard_group_leave;\n\treturn;\n\ncleanup:\n\tif (sway_group && sway_group->wlr_group) {\n\t\twlr_keyboard_group_destroy(sway_group->wlr_group);\n\t}\n\tfree(sway_group->seat_device->keyboard);\n\tfree(sway_group->seat_device->input_device);\n\tfree(sway_group->seat_device);\n\tfree(sway_group);\n}\n\nstatic void sway_keyboard_set_layout(struct sway_keyboard *keyboard,\n\t\t\t\t\t\t\t\t\t struct input_config *input_config) {\n\tstruct xkb_keymap *keymap = sway_keyboard_compile_keymap(input_config, NULL);\n\tif (!keymap) {\n\t\tsway_log(SWAY_ERROR, \"Failed to compile keymap. Attempting defaults\");\n\t\tkeymap = sway_keyboard_compile_keymap(NULL, NULL);\n\t\tif (!keymap) {\n\t\t\tsway_log(SWAY_ERROR,\n\t\t\t\t\t\"Failed to compile default keymap. Aborting configure\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tbool keymap_changed = keyboard->keymap ?\n\t\t!wlr_keyboard_keymaps_match(keyboard->keymap, keymap) : true;\n\tbool effective_layout_changed = keyboard->effective_layout != 0;\n\n\tif (keymap_changed || config->reloading) {\n\t\txkb_keymap_unref(keyboard->keymap);\n\t\tkeyboard->keymap = keymap;\n\t\tkeyboard->effective_layout = 0;\n\n\t\tsway_keyboard_group_remove_invalid(keyboard);\n\t\twlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap);\n\t\tif (!keyboard->wlr->group) {\n\t\t\tsway_keyboard_group_add(keyboard);\n\t\t}\n\n\t\txkb_mod_mask_t locked_mods = 0;\n\t\tif (input_config && input_config->xkb_numlock > 0) {\n\t\t\txkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap,\n\t\t\t\t\tXKB_MOD_NAME_NUM);\n\t\t\tif (mod_index != XKB_MOD_INVALID) {\n\t\t\t\tlocked_mods |= (uint32_t)1 << mod_index;\n\t\t\t}\n\t\t}\n\t\tif (input_config && input_config->xkb_capslock > 0) {\n\t\t\txkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap,\n\t\t\t\t\tXKB_MOD_NAME_CAPS);\n\t\t\tif (mod_index != XKB_MOD_INVALID) {\n\t\t\t\tlocked_mods |= (uint32_t)1 << mod_index;\n\t\t\t}\n\t\t}\n\t\tif (locked_mods) {\n\t\t\twlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0,\n\t\t\t\t\tlocked_mods, 0);\n\t\t\tuint32_t leds = 0;\n\t\t\tfor (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {\n\t\t\t\tif (xkb_state_led_index_is_active(keyboard->wlr->xkb_state,\n\t\t\t\t\t\tkeyboard->wlr->led_indexes[i])) {\n\t\t\t\t\tleds |= (1 << i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (keyboard->wlr->group) {\n\t\t\t\twlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds);\n\t\t\t} else {\n\t\t\t\twlr_keyboard_led_update(keyboard->wlr, leds);\n\t\t\t}\n\t\t}\n\t} else {\n\t\txkb_keymap_unref(keymap);\n\t\tsway_keyboard_group_remove_invalid(keyboard);\n\t\tif (!keyboard->wlr->group) {\n\t\t\tsway_keyboard_group_add(keyboard);\n\t\t}\n\t}\n\n\tif (keymap_changed) {\n\t\tipc_event_input(\"xkb_keymap\",\n\t\t\t\t  keyboard->seat_device->input_device);\n\t} else if (effective_layout_changed) {\n\t\tipc_event_input(\"xkb_layout\",\n\t\t\t\t  keyboard->seat_device->input_device);\n\t}\n}\n\nvoid sway_keyboard_configure(struct sway_keyboard *keyboard) {\n\tstruct input_config *input_config =\n\t\tinput_device_get_config(keyboard->seat_device->input_device);\n\n\tif (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr),\n\t\t\t\t\"sway_keyboard_configure should not be called with a \"\n\t\t\t\t\"keyboard group's keyboard\")) {\n\t\treturn;\n\t}\n\n\tint repeat_rate = 25;\n\tif (input_config && input_config->repeat_rate != INT_MIN) {\n\t\trepeat_rate = input_config->repeat_rate;\n\t}\n\tint repeat_delay = 600;\n\tif (input_config && input_config->repeat_delay != INT_MIN) {\n\t\trepeat_delay = input_config->repeat_delay;\n\t}\n\n\tbool repeat_info_changed = keyboard->repeat_rate != repeat_rate ||\n\t\tkeyboard->repeat_delay != repeat_delay;\n\n\tif (repeat_info_changed || config->reloading) {\n\t\tkeyboard->repeat_rate = repeat_rate;\n\t\tkeyboard->repeat_delay = repeat_delay;\n\n\t\twlr_keyboard_set_repeat_info(keyboard->wlr,\n\t\t\t\tkeyboard->repeat_rate, keyboard->repeat_delay);\n\t}\n\n\tif (!keyboard->seat_device->input_device->is_virtual) {\n\t\tsway_keyboard_set_layout(keyboard, input_config);\n\t}\n\n\t// If the seat has no active keyboard, set this one\n\tstruct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;\n\tstruct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;\n\tif (current_keyboard == NULL) {\n\t\twlr_seat_set_keyboard(seat, keyboard->wlr);\n\t}\n\n\twl_list_remove(&keyboard->keyboard_key.link);\n\twl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);\n\tkeyboard->keyboard_key.notify = handle_keyboard_key;\n\n\twl_list_remove(&keyboard->keyboard_modifiers.link);\n\twl_signal_add(&keyboard->wlr->events.modifiers,\n\t\t&keyboard->keyboard_modifiers);\n\tkeyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;\n\n}\n\nvoid sway_keyboard_destroy(struct sway_keyboard *keyboard) {\n\tif (!keyboard) {\n\t\treturn;\n\t}\n\tif (keyboard->wlr->group) {\n\t\tsway_keyboard_group_remove(keyboard);\n\t}\n\tstruct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;\n\tif (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) {\n\t\twlr_seat_set_keyboard(wlr_seat, NULL);\n\t}\n\tif (keyboard->keymap) {\n\t\txkb_keymap_unref(keyboard->keymap);\n\t}\n\twl_list_remove(&keyboard->keyboard_key.link);\n\twl_list_remove(&keyboard->keyboard_modifiers.link);\n\tsway_keyboard_disarm_key_repeat(keyboard);\n\twl_event_source_remove(keyboard->key_repeat_source);\n\tfree(keyboard);\n}\n"
  },
  {
    "path": "sway/input/libinput.c",
    "content": "#include <float.h>\n#include <libinput.h>\n#include <libudev.h>\n#include <limits.h>\n#include <wlr/backend/libinput.h>\n#include \"log.h\"\n#include \"sway/config.h\"\n#include \"sway/output.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/ipc-server.h\"\n\nstatic void log_status(enum libinput_config_status status) {\n\tif (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {\n\t\tsway_log(SWAY_ERROR, \"Failed to apply libinput config: %s\",\n\t\t\tlibinput_config_status_to_str(status));\n\t}\n}\n\nstatic bool set_send_events(struct libinput_device *device, uint32_t mode) {\n\tif (libinput_device_config_send_events_get_mode(device) == mode) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"send_events_set_mode(%\" PRIu32 \")\", mode);\n\tlog_status(libinput_device_config_send_events_set_mode(device, mode));\n\treturn true;\n}\n\nstatic bool set_tap(struct libinput_device *device,\n\t\tenum libinput_config_tap_state tap) {\n\tif (libinput_device_config_tap_get_finger_count(device) <= 0 ||\n\t\t\tlibinput_device_config_tap_get_enabled(device) == tap) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"tap_set_enabled(%d)\", tap);\n\tlog_status(libinput_device_config_tap_set_enabled(device, tap));\n\treturn true;\n}\n\nstatic bool set_tap_button_map(struct libinput_device *device,\n\t\tenum libinput_config_tap_button_map map) {\n\tif (libinput_device_config_tap_get_finger_count(device) <= 0 ||\n\t\t\tlibinput_device_config_tap_get_button_map(device) == map) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"tap_set_button_map(%d)\", map);\n\tlog_status(libinput_device_config_tap_set_button_map(device, map));\n\treturn true;\n}\n\nstatic bool set_tap_drag(struct libinput_device *device,\n\t\tenum libinput_config_drag_state drag) {\n\tif (libinput_device_config_tap_get_finger_count(device) <= 0 ||\n\t\t\tlibinput_device_config_tap_get_drag_enabled(device) == drag) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"tap_set_drag_enabled(%d)\", drag);\n\tlog_status(libinput_device_config_tap_set_drag_enabled(device, drag));\n\treturn true;\n}\n\nstatic bool set_tap_drag_lock(struct libinput_device *device,\n\t\tenum libinput_config_drag_lock_state lock) {\n\tif (libinput_device_config_tap_get_finger_count(device) <= 0 ||\n\t\t\tlibinput_device_config_tap_get_drag_lock_enabled(device) == lock) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"tap_set_drag_lock_enabled(%d)\", lock);\n\tlog_status(libinput_device_config_tap_set_drag_lock_enabled(device, lock));\n\treturn true;\n}\n\nstatic bool set_accel_speed(struct libinput_device *device, double speed) {\n\tif (!libinput_device_config_accel_is_available(device) ||\n\t\t\tlibinput_device_config_accel_get_speed(device) == speed) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"accel_set_speed(%f)\", speed);\n\tlog_status(libinput_device_config_accel_set_speed(device, speed));\n\treturn true;\n}\n\nstatic bool set_rotation_angle(struct libinput_device *device, double angle) {\n\tif (!libinput_device_config_rotation_is_available(device) ||\n\t\t\tlibinput_device_config_rotation_get_angle(device) == angle) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"rotation_set_angle(%f)\", angle);\n\tlog_status(libinput_device_config_rotation_set_angle(device, angle));\n\treturn true;\n}\n\nstatic bool set_accel_profile(struct libinput_device *device,\n\t\tenum libinput_config_accel_profile profile) {\n\tif (!libinput_device_config_accel_is_available(device) ||\n\t\t\tlibinput_device_config_accel_get_profile(device) == profile) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"accel_set_profile(%d)\", profile);\n\tlog_status(libinput_device_config_accel_set_profile(device, profile));\n\treturn true;\n}\n\nstatic bool set_natural_scroll(struct libinput_device *d, bool n) {\n\tif (!libinput_device_config_scroll_has_natural_scroll(d) ||\n\t\t\tlibinput_device_config_scroll_get_natural_scroll_enabled(d) == n) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"scroll_set_natural_scroll(%d)\", n);\n\tlog_status(libinput_device_config_scroll_set_natural_scroll_enabled(d, n));\n\treturn true;\n}\n\nstatic bool set_left_handed(struct libinput_device *device, bool left) {\n\tif (!libinput_device_config_left_handed_is_available(device) ||\n\t\t\tlibinput_device_config_left_handed_get(device) == left) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"left_handed_set(%d)\", left);\n\tlog_status(libinput_device_config_left_handed_set(device, left));\n\treturn true;\n}\n\nstatic bool set_click_method(struct libinput_device *device,\n\t\tenum libinput_config_click_method method) {\n\tuint32_t click = libinput_device_config_click_get_methods(device);\n\tif ((click & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) == 0 ||\n\t\t\tlibinput_device_config_click_get_method(device) == method) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"click_set_method(%d)\", method);\n\tlog_status(libinput_device_config_click_set_method(device, method));\n\treturn true;\n}\n\nstatic bool set_clickfinger_button_map(struct libinput_device *device,\n\t\tenum libinput_config_clickfinger_button_map map) {\n\tif (libinput_device_config_click_get_clickfinger_button_map(device) == map) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"clickfinger_set_button_map(%d)\", map);\n\tlog_status(libinput_device_config_click_set_clickfinger_button_map(device, map));\n\treturn true;\n}\n\nstatic bool set_middle_emulation(struct libinput_device *dev,\n\t\tenum libinput_config_middle_emulation_state mid) {\n\tif (!libinput_device_config_middle_emulation_is_available(dev) ||\n\t\t\tlibinput_device_config_middle_emulation_get_enabled(dev) == mid) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"middle_emulation_set_enabled(%d)\", mid);\n\tlog_status(libinput_device_config_middle_emulation_set_enabled(dev, mid));\n\treturn true;\n}\n\nstatic bool set_scroll_method(struct libinput_device *device,\n\t\tenum libinput_config_scroll_method method) {\n\tuint32_t scroll = libinput_device_config_scroll_get_methods(device);\n\tif ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 ||\n\t\t\tlibinput_device_config_scroll_get_method(device) == method) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"scroll_set_method(%d)\", method);\n\tlog_status(libinput_device_config_scroll_set_method(device, method));\n\treturn true;\n}\n\nstatic bool set_scroll_button(struct libinput_device *dev, uint32_t button) {\n\tuint32_t scroll = libinput_device_config_scroll_get_methods(dev);\n\tif ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 ||\n\t\t\tlibinput_device_config_scroll_get_button(dev) == button) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"scroll_set_button(%\" PRIu32 \")\", button);\n\tlog_status(libinput_device_config_scroll_set_button(dev, button));\n\treturn true;\n}\n\nstatic bool set_scroll_button_lock(struct libinput_device *dev,\n\t\tenum libinput_config_scroll_button_lock_state lock) {\n\tuint32_t scroll = libinput_device_config_scroll_get_methods(dev);\n\tif ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 ||\n\t\t\tlibinput_device_config_scroll_get_button_lock(dev) == lock) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"scroll_set_button_lock(%\" PRIu32 \")\", lock);\n\tlog_status(libinput_device_config_scroll_set_button_lock(dev, lock));\n\treturn true;\n}\n\nstatic bool set_dwt(struct libinput_device *device, bool dwt) {\n\tif (!libinput_device_config_dwt_is_available(device) ||\n\t\t\tlibinput_device_config_dwt_get_enabled(device) == dwt) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"dwt_set_enabled(%d)\", dwt);\n\tlog_status(libinput_device_config_dwt_set_enabled(device, dwt));\n\treturn true;\n}\n\nstatic bool set_dwtp(struct libinput_device *device, bool dwtp) {\n\tif (!libinput_device_config_dwtp_is_available(device) ||\n\t\t\tlibinput_device_config_dwtp_get_enabled(device) == dwtp) {\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"dwtp_set_enabled(%d)\", dwtp);\n\tlog_status(libinput_device_config_dwtp_set_enabled(device, dwtp));\n\treturn true;\n}\n\nstatic bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {\n\tif (!libinput_device_config_calibration_has_matrix(dev)) {\n\t\treturn false;\n\t}\n\tbool changed = false;\n\tfloat current[6];\n\tlibinput_device_config_calibration_get_matrix(dev, current);\n\tfor (int i = 0; i < 6; i++) {\n\t\tif (current[i] != mat[i]) {\n\t\t\tchanged = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (changed) {\n\t\tsway_log(SWAY_DEBUG, \"calibration_set_matrix(%f, %f, %f, %f, %f, %f)\",\n\t\t\t\tmat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);\n\t\tlog_status(libinput_device_config_calibration_set_matrix(dev, mat));\n\t}\n\treturn changed;\n}\n\nstatic bool configure_send_events(struct libinput_device *device,\n\t\tstruct input_config *ic) {\n\tif (ic->mapped_to_output &&\n\t\t\tstrcmp(\"*\", ic->mapped_to_output) != 0 &&\n\t\t\t!output_by_name_or_id(ic->mapped_to_output)) {\n\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\"%s '%s' is mapped to offline output '%s'; disabling input\",\n\t\t\t\tic->input_type, ic->identifier, ic->mapped_to_output);\n\t\treturn set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);\n\t} else if (ic->send_events != INT_MIN) {\n\t\treturn set_send_events(device, ic->send_events);\n\t} else {\n\t\t// Have to reset to the default mode here, otherwise if ic->send_events\n\t\t// is unset and a mapped output just came online after being disabled,\n\t\t// we'd remain stuck sending no events.\n\t\treturn set_send_events(device,\n\t\t\tlibinput_device_config_send_events_get_default_mode(device));\n\t}\n}\n\nbool sway_input_configure_libinput_device(struct sway_input_device *input_device) {\n\tstruct input_config *ic = input_device_get_config(input_device);\n\tif (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {\n\t\treturn false;\n\t}\n\n\tstruct libinput_device *device =\n\t\twlr_libinput_get_device_handle(input_device->wlr_device);\n\tsway_log(SWAY_DEBUG, \"sway_input_configure_libinput_device('%s' on '%s')\",\n\t\t\tic->identifier, input_device->identifier);\n\n\tbool changed = configure_send_events(device, ic);\n\tif (ic->tap != INT_MIN) {\n\t\tchanged |= set_tap(device, ic->tap);\n\t}\n\tif (ic->tap_button_map != INT_MIN) {\n\t\tchanged |= set_tap_button_map(device, ic->tap_button_map);\n\t}\n\tif (ic->drag != INT_MIN) {\n\t\tchanged |= set_tap_drag(device, ic->drag);\n\t}\n\tif (ic->drag_lock != INT_MIN) {\n\t\tchanged |= set_tap_drag_lock(device, ic->drag_lock);\n\t}\n\tif (ic->pointer_accel != FLT_MIN) {\n\t\tchanged |= set_accel_speed(device, ic->pointer_accel);\n\t}\n\tif (ic->rotation_angle != FLT_MIN) {\n\t\tchanged |= set_rotation_angle(device, ic->rotation_angle);\n\t}\n\tif (ic->accel_profile != INT_MIN) {\n\t\tchanged |= set_accel_profile(device, ic->accel_profile);\n\t}\n\tif (ic->natural_scroll != INT_MIN) {\n\t\tchanged |= set_natural_scroll(device, ic->natural_scroll);\n\t}\n\tif (ic->left_handed != INT_MIN) {\n\t\tchanged |= set_left_handed(device, ic->left_handed);\n\t}\n\tif (ic->click_method != INT_MIN) {\n\t\tchanged |= set_click_method(device, ic->click_method);\n\t}\n\tif (ic->clickfinger_button_map != INT_MIN) {\n\t\tchanged |= set_clickfinger_button_map(device, ic->clickfinger_button_map);\n\t}\n\tif (ic->middle_emulation != INT_MIN) {\n\t\tchanged |= set_middle_emulation(device, ic->middle_emulation);\n\t}\n\tif (ic->scroll_method != INT_MIN) {\n\t\tchanged |= set_scroll_method(device, ic->scroll_method);\n\t}\n\tif (ic->scroll_button != INT_MIN) {\n\t\tchanged |= set_scroll_button(device, ic->scroll_button);\n\t}\n\tif (ic->scroll_button_lock != INT_MIN) {\n\t\tchanged |= set_scroll_button_lock(device, ic->scroll_button_lock);\n\t}\n\tif (ic->dwt != INT_MIN) {\n\t\tchanged |= set_dwt(device, ic->dwt);\n\t}\n\tif (ic->dwtp != INT_MIN) {\n\t\tchanged |= set_dwtp(device, ic->dwtp);\n\t}\n\tif (ic->calibration_matrix.configured) {\n\t\tchanged |= set_calibration_matrix(device, ic->calibration_matrix.matrix);\n\t}\n\n\treturn changed;\n}\n\nvoid sway_input_configure_libinput_device_send_events(\n\t\tstruct sway_input_device *input_device) {\n\tstruct input_config *ic = input_device_get_config(input_device);\n\tif (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {\n\t\treturn;\n\t}\n\n\tstruct libinput_device *device =\n\t\twlr_libinput_get_device_handle(input_device->wlr_device);\n\tbool changed = configure_send_events(device, ic);\n\n\tif (changed) {\n\t\tipc_event_input(\"libinput_config\", input_device);\n\t}\n}\n\nvoid sway_input_reset_libinput_device(struct sway_input_device *input_device) {\n\tif (!wlr_input_device_is_libinput(input_device->wlr_device)) {\n\t\treturn;\n\t}\n\n\tstruct libinput_device *device =\n\t\twlr_libinput_get_device_handle(input_device->wlr_device);\n\tsway_log(SWAY_DEBUG, \"sway_input_reset_libinput_device(%s)\",\n\t\tinput_device->identifier);\n\tbool changed = false;\n\n\tchanged |= set_send_events(device,\n\t\tlibinput_device_config_send_events_get_default_mode(device));\n\tchanged |= set_tap(device,\n\t\tlibinput_device_config_tap_get_default_enabled(device));\n\tchanged |= set_tap_button_map(device,\n\t\tlibinput_device_config_tap_get_default_button_map(device));\n\tchanged |= set_tap_drag(device,\n\t\tlibinput_device_config_tap_get_default_drag_enabled(device));\n\tchanged |= set_tap_drag_lock(device,\n\t\tlibinput_device_config_tap_get_default_drag_lock_enabled(device));\n\tchanged |= set_accel_speed(device,\n\t\tlibinput_device_config_accel_get_default_speed(device));\n\tchanged |= set_rotation_angle(device,\n\t\tlibinput_device_config_rotation_get_default_angle(device));\n\tchanged |= set_accel_profile(device,\n\t\tlibinput_device_config_accel_get_default_profile(device));\n\tchanged |= set_natural_scroll(device,\n\t\tlibinput_device_config_scroll_get_default_natural_scroll_enabled(\n\t\tdevice));\n\tchanged |= set_left_handed(device,\n\t\tlibinput_device_config_left_handed_get_default(device));\n\tchanged |= set_click_method(device,\n\t\tlibinput_device_config_click_get_default_method(device));\n\tchanged |= set_clickfinger_button_map(device,\n\t\tlibinput_device_config_click_get_default_clickfinger_button_map(device));\n\tchanged |= set_middle_emulation(device,\n\t\tlibinput_device_config_middle_emulation_get_default_enabled(device));\n\tchanged |= set_scroll_method(device,\n\t\tlibinput_device_config_scroll_get_default_method(device));\n\tchanged |= set_scroll_button(device,\n\t\tlibinput_device_config_scroll_get_default_button(device));\n\tchanged |= set_dwt(device,\n\t\tlibinput_device_config_dwt_get_default_enabled(device));\n\tchanged |= set_dwtp(device,\n\t\tlibinput_device_config_dwtp_get_default_enabled(device));\n\n\tfloat matrix[6];\n\tlibinput_device_config_calibration_get_default_matrix(device, matrix);\n\tchanged |= set_calibration_matrix(device, matrix);\n\n\tif (changed) {\n\t\tipc_event_input(\"libinput_config\", input_device);\n\t}\n}\n\nstatic bool sway_udev_device_is_builtin(struct udev_device *udev_device) {\n\tconst char *id_path = udev_device_get_property_value(udev_device, \"ID_PATH\");\n\tif (!id_path) {\n\t\treturn false;\n\t}\n\n\tif (has_prefix(id_path, \"platform-\")) {\n\t\treturn true;\n\t}\n\n\treturn has_prefix(id_path, \"pci-\") && strstr(id_path, \"-platform-\");\n}\n\nbool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) {\n\tif (!wlr_input_device_is_libinput(sway_device->wlr_device)) {\n\t\treturn false;\n\t}\n\n\tstruct libinput_device *device =\n\t\twlr_libinput_get_device_handle(sway_device->wlr_device);\n\tstruct udev_device *udev_device =\n\t\tlibinput_device_get_udev_device(device);\n\tif (!udev_device) {\n\t\treturn false;\n\t}\n\n\tbool is_builtin = sway_udev_device_is_builtin(udev_device);\n\tudev_device_unref(udev_device);\n\treturn is_builtin;\n}\n"
  },
  {
    "path": "sway/input/seat.c",
    "content": "#include <assert.h>\n#include <linux/input-event-codes.h>\n#include <string.h>\n#include <strings.h>\n#include <time.h>\n#include <wlr/config.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_data_device.h>\n#include <wlr/types/wlr_idle_notify_v1.h>\n#include <wlr/types/wlr_keyboard_group.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_primary_selection.h>\n#include <wlr/types/wlr_tablet_v2.h>\n#include <wlr/types/wlr_touch.h>\n#include <wlr/types/wlr_xcursor_manager.h>\n#include \"config.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"sway/config.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/input/libinput.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/input/switch.h\"\n#include \"sway/input/tablet.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/layers.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n\nstatic void seat_device_destroy(struct sway_seat_device *seat_device) {\n\tif (!seat_device) {\n\t\treturn;\n\t}\n\n\tsway_keyboard_destroy(seat_device->keyboard);\n\tsway_tablet_destroy(seat_device->tablet);\n\tsway_tablet_pad_destroy(seat_device->tablet_pad);\n\tsway_switch_destroy(seat_device->switch_device);\n\twlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,\n\t\tseat_device->input_device->wlr_device);\n\twl_list_remove(&seat_device->link);\n\tfree(seat_device);\n}\n\nstatic void seat_node_destroy(struct sway_seat_node *seat_node) {\n\twl_list_remove(&seat_node->destroy.link);\n\twl_list_remove(&seat_node->link);\n\n\t/*\n\t * This is the only time we remove items from the focus stack without\n\t * immediately re-adding them. If we just removed the last thing,\n\t * mark that nothing has focus anymore.\n\t */\n\tif (wl_list_empty(&seat_node->seat->focus_stack)) {\n\t\tseat_node->seat->has_focus = false;\n\t}\n\n\tfree(seat_node);\n}\n\nvoid seat_destroy(struct sway_seat *seat) {\n\twlr_seat_destroy(seat->wlr_seat);\n}\n\nstatic void handle_seat_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_seat *seat = wl_container_of(listener, seat, destroy);\n\n\tif (seat == config->handler_context.seat) {\n\t\tconfig->handler_context.seat = input_manager_get_default_seat();\n\t}\n\tstruct sway_seat_device *seat_device, *next;\n\twl_list_for_each_safe(seat_device, next, &seat->devices, link) {\n\t\tseat_device_destroy(seat_device);\n\t}\n\tstruct sway_seat_node *seat_node, *next_seat_node;\n\twl_list_for_each_safe(seat_node, next_seat_node, &seat->focus_stack,\n\t\t\tlink) {\n\t\tseat_node_destroy(seat_node);\n\t}\n\tsway_input_method_relay_finish(&seat->im_relay);\n\tsway_cursor_destroy(seat->cursor);\n\twl_list_remove(&seat->new_node.link);\n\twl_list_remove(&seat->request_start_drag.link);\n\twl_list_remove(&seat->start_drag.link);\n\twl_list_remove(&seat->request_set_selection.link);\n\twl_list_remove(&seat->request_set_primary_selection.link);\n\twl_list_remove(&seat->link);\n\twl_list_remove(&seat->destroy.link);\n\tfor (int i = 0; i < seat->deferred_bindings->length; i++) {\n\t\tfree_sway_binding(seat->deferred_bindings->items[i]);\n\t}\n\twlr_scene_node_destroy(&seat->scene_tree->node);\n\tlist_free(seat->deferred_bindings);\n\tfree(seat->prev_workspace_name);\n\tfree(seat);\n}\n\nvoid seat_idle_notify_activity(struct sway_seat *seat,\n\t\tenum sway_input_idle_source source) {\n\tif ((source & seat->idle_inhibit_sources) == 0) {\n\t\treturn;\n\t}\n\twlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat);\n}\n\n/**\n * Activate all views within this container recursively.\n */\nstatic void seat_send_activate(struct sway_node *node, struct sway_seat *seat) {\n\tif (node_is_view(node)) {\n\t\tif (!seat_is_input_allowed(seat, node->sway_container->view->surface)) {\n\t\t\tsway_log(SWAY_DEBUG, \"Refusing to set focus, input is inhibited\");\n\t\t\treturn;\n\t\t}\n\t\tview_set_activated(node->sway_container->view, true);\n\t} else {\n\t\tlist_t *children = node_get_children(node);\n\t\tfor (int i = 0; i < children->length; ++i) {\n\t\t\tstruct sway_container *child = children->items[i];\n\t\t\tseat_send_activate(&child->node, seat);\n\t\t}\n\t}\n}\n\nstatic struct sway_keyboard *sway_keyboard_for_wlr_keyboard(\n\t\tstruct sway_seat *seat, struct wlr_keyboard *wlr_keyboard) {\n\tstruct sway_seat_device *seat_device;\n\twl_list_for_each(seat_device, &seat->devices, link) {\n\t\tstruct sway_input_device *input_device = seat_device->input_device;\n\t\tif (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (input_device->wlr_device == &wlr_keyboard->base) {\n\t\t\treturn seat_device->keyboard;\n\t\t}\n\t}\n\tstruct sway_keyboard_group *group;\n\twl_list_for_each(group, &seat->keyboard_groups, link) {\n\t\tstruct sway_input_device *input_device =\n\t\t\tgroup->seat_device->input_device;\n\t\tif (input_device->wlr_device == &wlr_keyboard->base) {\n\t\t\treturn group->seat_device->keyboard;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void seat_keyboard_notify_enter(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface) {\n\tstruct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);\n\tif (!keyboard) {\n\t\twlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL);\n\t\treturn;\n\t}\n\n\tstruct sway_keyboard *sway_keyboard =\n\t\tsway_keyboard_for_wlr_keyboard(seat, keyboard);\n\tassert(sway_keyboard && \"Cannot find sway_keyboard for seat keyboard\");\n\n\tstruct sway_shortcut_state *state = &sway_keyboard->state_pressed_sent;\n\twlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,\n\t\t\tstate->pressed_keycodes, state->npressed, &keyboard->modifiers);\n}\n\nstatic void seat_tablet_pads_set_focus(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface) {\n\tstruct sway_seat_device *seat_device;\n\twl_list_for_each(seat_device, &seat->devices, link) {\n\t\tsway_tablet_pad_set_focus(seat_device->tablet_pad, surface);\n\t}\n}\n\n/**\n * If con is a view, set it as active and enable keyboard input.\n * If con is a container, set all child views as active and don't enable\n * keyboard input on any.\n */\nstatic void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {\n\tseat_send_activate(node, seat);\n\n\tstruct sway_view *view = node->type == N_CONTAINER ?\n\t\tnode->sway_container->view : NULL;\n\n\tif (view && seat_is_input_allowed(seat, view->surface)) {\n#if WLR_HAS_XWAYLAND\n\t\tif (view->type == SWAY_VIEW_XWAYLAND) {\n\t\t\tstruct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;\n\t\t\twlr_xwayland_set_seat(xwayland, seat->wlr_seat);\n\t\t}\n#endif\n\n\t\tseat_keyboard_notify_enter(seat, view->surface);\n\t\tseat_tablet_pads_set_focus(seat, view->surface);\n\t\tsway_input_method_relay_set_focus(&seat->im_relay, view->surface);\n\n\t\tstruct wlr_pointer_constraint_v1 *constraint =\n\t\t\twlr_pointer_constraints_v1_constraint_for_surface(\n\t\t\t\tserver.pointer_constraints, view->surface, seat->wlr_seat);\n\t\tsway_cursor_constrain(seat->cursor, constraint);\n\t}\n}\n\nvoid seat_for_each_node(struct sway_seat *seat,\n\t\tvoid (*f)(struct sway_node *node, void *data), void *data) {\n\tstruct sway_seat_node *current = NULL;\n\twl_list_for_each(current, &seat->focus_stack, link) {\n\t\tf(current->node, data);\n\t}\n}\n\nstruct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,\n\t\tstruct sway_node *ancestor) {\n\tif (node_is_view(ancestor)) {\n\t\treturn ancestor->sway_container;\n\t}\n\tstruct sway_seat_node *current;\n\twl_list_for_each(current, &seat->focus_stack, link) {\n\t\tstruct sway_node *node = current->node;\n\t\tif (node_is_view(node) && node_has_ancestor(node, ancestor)) {\n\t\t\treturn node->sway_container;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void handle_seat_node_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_seat_node *seat_node =\n\t\twl_container_of(listener, seat_node, destroy);\n\tstruct sway_seat *seat = seat_node->seat;\n\tstruct sway_node *node = seat_node->node;\n\tstruct sway_node *parent = node_get_parent(node);\n\tstruct sway_node *focus = seat_get_focus(seat);\n\n\tif (node->type == N_WORKSPACE) {\n\t\tseat_node_destroy(seat_node);\n\t\t// If an unmanaged or layer surface is focused when an output gets\n\t\t// disabled and an empty workspace on the output was focused by the\n\t\t// seat, the seat needs to refocus its focus inactive to update the\n\t\t// value of seat->workspace.\n\t\tif (seat->workspace == node->sway_workspace) {\n\t\t\tstruct sway_node *node = seat_get_focus_inactive(seat, &root->node);\n\t\t\tseat_set_focus(seat, NULL);\n\t\t\tif (node) {\n\t\t\t\tseat_set_focus(seat, node);\n\t\t\t} else {\n\t\t\t\tseat->workspace = NULL;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\t// Even though the container being destroyed might be nowhere near the\n\t// focused container, we still need to set focus_inactive on a sibling of\n\t// the container being destroyed.\n\tbool needs_new_focus = focus &&\n\t\t(focus == node || node_has_ancestor(focus, node));\n\n\tseat_node_destroy(seat_node);\n\n\tif (!parent && !needs_new_focus) {\n\t\t// Destroying a container that is no longer in the tree\n\t\treturn;\n\t}\n\n\t// Find new focus_inactive (ie. sibling, or workspace if no siblings left)\n\tstruct sway_node *next_focus = NULL;\n\twhile (next_focus == NULL && parent != NULL) {\n\t\tstruct sway_container *con =\n\t\t\tseat_get_focus_inactive_view(seat, parent);\n\t\tnext_focus = con ? &con->node : NULL;\n\n\t\tif (next_focus == NULL && parent->type == N_WORKSPACE) {\n\t\t\tnext_focus = parent;\n\t\t\tbreak;\n\t\t}\n\n\t\tparent = node_get_parent(parent);\n\t}\n\n\tif (!next_focus) {\n\t\tstruct sway_workspace *ws = seat_get_last_known_workspace(seat);\n\t\tif (!ws) {\n\t\t\treturn;\n\t\t}\n\t\tstruct sway_container *con =\n\t\t\tseat_get_focus_inactive_view(seat, &ws->node);\n\t\tnext_focus = con ? &(con->node) : &(ws->node);\n\t}\n\n\tif (next_focus->type == N_WORKSPACE &&\n\t\t\t!workspace_is_visible(next_focus->sway_workspace)) {\n\t\t// Do not change focus to a non-visible workspace\n\t\treturn;\n\t}\n\n\tif (needs_new_focus) {\n\t\t// Make sure the workspace IPC event gets sent\n\t\tif (node->type == N_CONTAINER && node->sway_container->scratchpad) {\n\t\t\tseat_set_focus(seat, NULL);\n\t\t}\n\t\t// The structure change might have caused it to move up to the top of\n\t\t// the focus stack without sending focus notifications to the view\n\t\tif (seat_get_focus(seat) == next_focus) {\n\t\t\tseat_send_focus(next_focus, seat);\n\t\t} else {\n\t\t\tseat_set_focus(seat, next_focus);\n\t\t}\n\t} else {\n\t\t// Setting focus_inactive\n\t\tfocus = seat_get_focus_inactive(seat, &root->node);\n\t\tseat_set_raw_focus(seat, next_focus);\n\t\tif (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) {\n\t\t\tseat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node);\n\t\t}\n\t\tseat_set_raw_focus(seat, focus);\n\t}\n}\n\nstatic struct sway_seat_node *seat_node_from_node(\n\t\tstruct sway_seat *seat, struct sway_node *node) {\n\tif (node->type == N_ROOT || node->type == N_OUTPUT) {\n\t\t// these don't get seat nodes ever\n\t\treturn NULL;\n\t}\n\n\tstruct sway_seat_node *seat_node = NULL;\n\twl_list_for_each(seat_node, &seat->focus_stack, link) {\n\t\tif (seat_node->node == node) {\n\t\t\treturn seat_node;\n\t\t}\n\t}\n\n\tseat_node = calloc(1, sizeof(struct sway_seat_node));\n\tif (seat_node == NULL) {\n\t\tsway_log(SWAY_ERROR, \"could not allocate seat node\");\n\t\treturn NULL;\n\t}\n\n\tseat_node->node = node;\n\tseat_node->seat = seat;\n\twl_list_insert(seat->focus_stack.prev, &seat_node->link);\n\twl_signal_add(&node->events.destroy, &seat_node->destroy);\n\tseat_node->destroy.notify = handle_seat_node_destroy;\n\n\treturn seat_node;\n}\n\nstatic void handle_new_node(struct wl_listener *listener, void *data) {\n\tstruct sway_seat *seat = wl_container_of(listener, seat, new_node);\n\tstruct sway_node *node = data;\n\tseat_node_from_node(seat, node);\n}\n\nstatic void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) {\n\tstruct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON);\n\tstruct wlr_cursor *cursor = seat->cursor->cursor;\n\n\tswitch (wlr_icon->drag->grab_type) {\n\tcase WLR_DRAG_GRAB_KEYBOARD:\n\t\treturn;\n\tcase WLR_DRAG_GRAB_KEYBOARD_POINTER:\n\t\twlr_scene_node_set_position(node, cursor->x, cursor->y);\n\t\tbreak;\n\tcase WLR_DRAG_GRAB_KEYBOARD_TOUCH:;\n\t\tstruct wlr_touch_point *point =\n\t\t\twlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->drag->touch_id);\n\t\tif (point == NULL) {\n\t\t\treturn;\n\t\t}\n\t\twlr_scene_node_set_position(node, seat->touch_x, seat->touch_y);\n\t}\n}\n\nvoid drag_icons_update_position(struct sway_seat *seat) {\n\tstruct wlr_scene_node *node;\n\twl_list_for_each(node, &seat->drag_icons->children, link) {\n\t\tdrag_icon_update_position(seat, node);\n\t}\n}\n\nstatic void drag_handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_drag *drag = wl_container_of(listener, drag, destroy);\n\n\t// Focus enter isn't sent during drag, so refocus the focused node, layer\n\t// surface or unmanaged surface.\n\tstruct sway_seat *seat = drag->seat;\n\tstruct sway_node *focus = seat_get_focus(seat);\n\tif (focus) {\n\t\tseat_set_focus(seat, NULL);\n\t\tseat_set_focus(seat, focus);\n\t} else if (seat->focused_layer) {\n\t\tstruct wlr_layer_surface_v1 *layer = seat->focused_layer;\n\t\tseat_set_focus_layer(seat, NULL);\n\t\tseat_set_focus_layer(seat, layer);\n\t} else {\n\t\tstruct wlr_surface *unmanaged = seat->wlr_seat->keyboard_state.focused_surface;\n\t\tseat_set_focus_surface(seat, NULL, false);\n\t\tseat_set_focus_surface(seat, unmanaged, false);\n\t}\n\n\tdrag->wlr_drag->data = NULL;\n\twl_list_remove(&drag->destroy.link);\n\tfree(drag);\n}\n\nstatic void handle_request_start_drag(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_seat *seat = wl_container_of(listener, seat, request_start_drag);\n\tstruct wlr_seat_request_start_drag_event *event = data;\n\n\tif (wlr_seat_validate_pointer_grab_serial(seat->wlr_seat,\n\t\t\tevent->origin, event->serial)) {\n\t\twlr_seat_start_pointer_drag(seat->wlr_seat, event->drag, event->serial);\n\t\treturn;\n\t}\n\n\tstruct wlr_touch_point *point;\n\tif (wlr_seat_validate_touch_grab_serial(seat->wlr_seat,\n\t\t\tevent->origin, event->serial, &point)) {\n\t\twlr_seat_start_touch_drag(seat->wlr_seat,\n\t\t\tevent->drag, event->serial, point);\n\t\treturn;\n\t}\n\n\t// TODO: tablet grabs\n\n\tsway_log(SWAY_DEBUG, \"Ignoring start_drag request: \"\n\t\t\"could not validate pointer or touch serial %\" PRIu32, event->serial);\n\twlr_data_source_destroy(event->drag->source);\n}\n\nstatic void handle_start_drag(struct wl_listener *listener, void *data) {\n\tstruct sway_seat *seat = wl_container_of(listener, seat, start_drag);\n\tstruct wlr_drag *wlr_drag = data;\n\n\tstruct sway_drag *drag = calloc(1, sizeof(struct sway_drag));\n\tif (drag == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Allocation failed\");\n\t\treturn;\n\t}\n\tdrag->seat = seat;\n\tdrag->wlr_drag = wlr_drag;\n\twlr_drag->data = drag;\n\n\tdrag->destroy.notify = drag_handle_destroy;\n\twl_signal_add(&wlr_drag->events.destroy, &drag->destroy);\n\n\tstruct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon;\n\tif (wlr_drag_icon != NULL) {\n\t\tstruct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon);\n\t\tif (!tree) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to allocate a drag icon scene tree\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON,\n\t\t\t\twlr_drag_icon)) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to allocate a drag icon scene descriptor\");\n\t\t\twlr_scene_node_destroy(&tree->node);\n\t\t\treturn;\n\t\t}\n\n\t\tdrag_icon_update_position(seat, &tree->node);\n\t}\n\tseatop_begin_default(seat);\n}\n\nstatic void handle_request_set_selection(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_seat *seat =\n\t\twl_container_of(listener, seat, request_set_selection);\n\tstruct wlr_seat_request_set_selection_event *event = data;\n\twlr_seat_set_selection(seat->wlr_seat, event->source, event->serial);\n}\n\nstatic void handle_request_set_primary_selection(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_seat *seat =\n\t\twl_container_of(listener, seat, request_set_primary_selection);\n\tstruct wlr_seat_request_set_primary_selection_event *event = data;\n\twlr_seat_set_primary_selection(seat->wlr_seat, event->source, event->serial);\n}\n\nstatic void collect_focus_iter(struct sway_node *node, void *data) {\n\tstruct sway_seat *seat = data;\n\tstruct sway_seat_node *seat_node = seat_node_from_node(seat, node);\n\tif (!seat_node) {\n\t\treturn;\n\t}\n\twl_list_remove(&seat_node->link);\n\twl_list_insert(&seat->focus_stack, &seat_node->link);\n}\n\nstatic void collect_focus_workspace_iter(struct sway_workspace *workspace,\n\t\tvoid *data) {\n\tcollect_focus_iter(&workspace->node, data);\n}\n\nstatic void collect_focus_container_iter(struct sway_container *container,\n\t\tvoid *data) {\n\tcollect_focus_iter(&container->node, data);\n}\n\nstruct sway_seat *seat_create(const char *seat_name) {\n\tstruct sway_seat *seat = calloc(1, sizeof(struct sway_seat));\n\tif (!seat) {\n\t\treturn NULL;\n\t}\n\n\tbool failed = false;\n\tseat->scene_tree = alloc_scene_tree(root->layers.seat, &failed);\n\tseat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed);\n\tif (failed) {\n\t\twlr_scene_node_destroy(&seat->scene_tree->node);\n\t\tfree(seat);\n\t\treturn NULL;\n\t}\n\n\tseat->wlr_seat = wlr_seat_create(server.wl_display, seat_name);\n\tif (!sway_assert(seat->wlr_seat, \"could not allocate seat\")) {\n\t\twlr_scene_node_destroy(&seat->scene_tree->node);\n\t\tfree(seat);\n\t\treturn NULL;\n\t}\n\tseat->wlr_seat->data = seat;\n\n\tseat->cursor = sway_cursor_create(seat);\n\tif (!seat->cursor) {\n\t\twlr_scene_node_destroy(&seat->scene_tree->node);\n\t\twlr_seat_destroy(seat->wlr_seat);\n\t\tfree(seat);\n\t\treturn NULL;\n\t}\n\n\tseat->destroy.notify = handle_seat_destroy;\n\twl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy);\n\n\tseat->idle_inhibit_sources = seat->idle_wake_sources =\n\t\tIDLE_SOURCE_KEYBOARD |\n\t\tIDLE_SOURCE_POINTER |\n\t\tIDLE_SOURCE_TOUCH |\n\t\tIDLE_SOURCE_TABLET_PAD |\n\t\tIDLE_SOURCE_TABLET_TOOL |\n\t\tIDLE_SOURCE_SWITCH;\n\n\t// init the focus stack\n\twl_list_init(&seat->focus_stack);\n\n\twl_list_init(&seat->devices);\n\n\troot_for_each_workspace(collect_focus_workspace_iter, seat);\n\troot_for_each_container(collect_focus_container_iter, seat);\n\n\tseat->deferred_bindings = create_list();\n\n\twl_signal_add(&root->events.new_node, &seat->new_node);\n\tseat->new_node.notify = handle_new_node;\n\n\twl_signal_add(&seat->wlr_seat->events.request_start_drag,\n\t\t&seat->request_start_drag);\n\tseat->request_start_drag.notify = handle_request_start_drag;\n\n\twl_signal_add(&seat->wlr_seat->events.start_drag, &seat->start_drag);\n\tseat->start_drag.notify = handle_start_drag;\n\n\twl_signal_add(&seat->wlr_seat->events.request_set_selection,\n\t\t&seat->request_set_selection);\n\tseat->request_set_selection.notify = handle_request_set_selection;\n\n\twl_signal_add(&seat->wlr_seat->events.request_set_primary_selection,\n\t\t&seat->request_set_primary_selection);\n\tseat->request_set_primary_selection.notify =\n\t\thandle_request_set_primary_selection;\n\n\twl_list_init(&seat->keyboard_groups);\n\twl_list_init(&seat->keyboard_shortcuts_inhibitors);\n\n\tsway_input_method_relay_init(seat, &seat->im_relay);\n\n\tbool first = wl_list_empty(&server.input->seats);\n\twl_list_insert(&server.input->seats, &seat->link);\n\n\tif (!first) {\n\t\t// Since this is not the first seat, attempt to set initial focus\n\t\tstruct sway_seat *current_seat = input_manager_current_seat();\n\t\tstruct sway_node *current_focus =\n\t\t\tseat_get_focus_inactive(current_seat, &root->node);\n\t\tseat_set_focus(seat, current_focus);\n\t}\n\n\tseatop_begin_default(seat);\n\n\treturn seat;\n}\n\nstatic void seat_update_capabilities(struct sway_seat *seat) {\n\tuint32_t caps = 0;\n\tuint32_t previous_caps = seat->wlr_seat->capabilities;\n\tstruct sway_seat_device *seat_device;\n\twl_list_for_each(seat_device, &seat->devices, link) {\n\t\tswitch (seat_device->input_device->wlr_device->type) {\n\t\tcase WLR_INPUT_DEVICE_KEYBOARD:\n\t\t\tcaps |= WL_SEAT_CAPABILITY_KEYBOARD;\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_POINTER:\n\t\t\tcaps |= WL_SEAT_CAPABILITY_POINTER;\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TOUCH:\n\t\t\tcaps |= WL_SEAT_CAPABILITY_TOUCH;\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TABLET:\n\t\t\tcaps |= WL_SEAT_CAPABILITY_POINTER;\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_SWITCH:\n\t\tcase WLR_INPUT_DEVICE_TABLET_PAD:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Hide cursor if seat doesn't have pointer capability.\n\t// We must call cursor_set_image while the wlr_seat has the capabilities\n\t// otherwise it's a no op.\n\tif ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {\n\t\tcursor_set_image(seat->cursor, NULL, NULL);\n\t\twlr_seat_set_capabilities(seat->wlr_seat, caps);\n\t} else {\n\t\twlr_seat_set_capabilities(seat->wlr_seat, caps);\n\t\tif ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) {\n\t\t\tcursor_set_image(seat->cursor, \"default\", NULL);\n\t\t}\n\t}\n}\n\nstatic void seat_reset_input_config(struct sway_seat *seat,\n\t\tstruct sway_seat_device *sway_device) {\n\tsway_log(SWAY_DEBUG, \"Resetting output mapping for input device %s\",\n\t\tsway_device->input_device->identifier);\n\twlr_cursor_map_input_to_output(seat->cursor->cursor,\n\t\tsway_device->input_device->wlr_device, NULL);\n}\n\n/**\n * Get the name of the built-in output, if any. Returns NULL if there isn't\n * exactly one built-in output.\n */\nstatic const char *get_builtin_output_name(void) {\n\tconst char *match = NULL;\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tconst char *name = output->wlr_output->name;\n\t\tif (has_prefix(name, \"eDP-\") || has_prefix(name, \"LVDS-\") ||\n\t\t\t\thas_prefix(name, \"DSI-\")) {\n\t\t\tif (match != NULL) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tmatch = name;\n\t\t}\n\t}\n\treturn match;\n}\n\nstatic bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) {\n\tswitch (seat_device->input_device->wlr_device->type) {\n\tcase WLR_INPUT_DEVICE_TOUCH:\n\tcase WLR_INPUT_DEVICE_TABLET:\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nstatic void seat_apply_input_mapping(struct sway_seat *seat,\n\t\tstruct sway_seat_device *sway_device) {\n\tstruct input_config *ic =\n\t\tinput_device_get_config(sway_device->input_device);\n\n\tswitch (sway_device->input_device->wlr_device->type) {\n\tcase WLR_INPUT_DEVICE_POINTER:\n\tcase WLR_INPUT_DEVICE_TOUCH:\n\tcase WLR_INPUT_DEVICE_TABLET:\n\t\tbreak;\n\tdefault:\n\t\treturn; // these devices don't support mappings\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Applying input mapping to %s\",\n\t\tsway_device->input_device->identifier);\n\n\tconst char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output;\n\tstruct wlr_box *mapped_to_region = ic == NULL ? NULL : ic->mapped_to_region;\n\tenum input_config_mapped_to mapped_to =\n\t\tic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to;\n\n\tswitch (mapped_to) {\n\tcase MAPPED_TO_DEFAULT:;\n\t\t/*\n\t\t * If the wlroots backend provides an output name, use that.\n\t\t *\n\t\t * Otherwise, try to map built-in touch and pointer devices to the\n\t\t * built-in output.\n\t\t */\n\t\tstruct wlr_input_device *dev = sway_device->input_device->wlr_device;\n\t\tswitch (dev->type) {\n\t\tcase WLR_INPUT_DEVICE_POINTER:\n\t\t\tmapped_to_output = wlr_pointer_from_input_device(dev)->output_name;\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TOUCH:\n\t\t\tmapped_to_output = wlr_touch_from_input_device(dev)->output_name;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmapped_to_output = NULL;\n\t\t\tbreak;\n\t\t}\n#if WLR_HAS_LIBINPUT_BACKEND\n\t\tif (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&\n\t\t\t\tsway_libinput_device_is_builtin(sway_device->input_device)) {\n\t\t\tmapped_to_output = get_builtin_output_name();\n\t\t\tif (mapped_to_output) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Auto-detected output '%s' for device '%s'\",\n\t\t\t\t\tmapped_to_output, sway_device->input_device->identifier);\n\t\t\t}\n\t\t}\n#else\n\t\t(void)is_touch_or_tablet_tool;\n\t\t(void)get_builtin_output_name;\n#endif\n\t\tif (mapped_to_output == NULL) {\n\t\t\treturn;\n\t\t}\n\t\t/* fallthrough */\n\tcase MAPPED_TO_OUTPUT:\n\t\tsway_log(SWAY_DEBUG, \"Mapping input device %s to output %s\",\n\t\t\tsway_device->input_device->identifier, mapped_to_output);\n\t\tif (strcmp(\"*\", mapped_to_output) == 0) {\n\t\t\twlr_cursor_map_input_to_output(seat->cursor->cursor,\n\t\t\t\tsway_device->input_device->wlr_device, NULL);\n\t\t\twlr_cursor_map_input_to_region(seat->cursor->cursor,\n\t\t\t\tsway_device->input_device->wlr_device, NULL);\n\t\t\tsway_log(SWAY_DEBUG, \"Reset output mapping\");\n\t\t\treturn;\n\t\t}\n\t\tstruct sway_output *output = output_by_name_or_id(mapped_to_output);\n\t\tif (!output) {\n\t\t\tsway_log(SWAY_DEBUG, \"Requested output %s for device %s isn't present\",\n\t\t\t\tmapped_to_output, sway_device->input_device->identifier);\n\t\t\treturn;\n\t\t}\n\t\twlr_cursor_map_input_to_output(seat->cursor->cursor,\n\t\t\tsway_device->input_device->wlr_device, output->wlr_output);\n\t\twlr_cursor_map_input_to_region(seat->cursor->cursor,\n\t\t\tsway_device->input_device->wlr_device, NULL);\n\t\tsway_log(SWAY_DEBUG,\n\t\t\t\"Mapped to output %s\", output->wlr_output->name);\n\t\treturn;\n\tcase MAPPED_TO_REGION:\n\t\tsway_log(SWAY_DEBUG, \"Mapping input device %s to %d,%d %dx%d\",\n\t\t\tsway_device->input_device->identifier,\n\t\t\tmapped_to_region->x, mapped_to_region->y,\n\t\t\tmapped_to_region->width, mapped_to_region->height);\n\t\twlr_cursor_map_input_to_output(seat->cursor->cursor,\n\t\t\tsway_device->input_device->wlr_device, NULL);\n\t\twlr_cursor_map_input_to_region(seat->cursor->cursor,\n\t\t\tsway_device->input_device->wlr_device, mapped_to_region);\n\t\treturn;\n\t}\n}\n\nstatic void seat_configure_pointer(struct sway_seat *seat,\n\t\tstruct sway_seat_device *sway_device) {\n\tseat_configure_xcursor(seat);\n\twlr_cursor_attach_input_device(seat->cursor->cursor,\n\t\tsway_device->input_device->wlr_device);\n\twl_event_source_timer_update(\n\t\t\tseat->cursor->hide_source, cursor_get_timeout(seat->cursor));\n}\n\nstatic void seat_configure_keyboard(struct sway_seat *seat,\n\t\tstruct sway_seat_device *seat_device) {\n\tif (!seat_device->keyboard) {\n\t\tsway_keyboard_create(seat, seat_device);\n\t}\n\tsway_keyboard_configure(seat_device->keyboard);\n\n\t// We only need to update the current keyboard, as the rest will be updated\n\t// as they are activated.\n\tstruct wlr_keyboard *wlr_keyboard =\n\t\twlr_keyboard_from_input_device(seat_device->input_device->wlr_device);\n\tstruct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard;\n\tif (wlr_keyboard != current_keyboard) {\n\t\treturn;\n\t}\n\n\t// Notify reenter to pick up the new configuration. This reuses\n\t// the current focused surface to avoid breaking input grabs.\n\tstruct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;\n\tif (surface) {\n\t\tseat_keyboard_notify_enter(seat, surface);\n\t}\n}\n\nstatic void seat_configure_switch(struct sway_seat *seat,\n\t\tstruct sway_seat_device *seat_device) {\n\tif (!seat_device->switch_device) {\n\t\tsway_switch_create(seat, seat_device);\n\t}\n\tsway_switch_configure(seat_device->switch_device);\n}\n\nstatic void seat_configure_touch(struct sway_seat *seat,\n\t\tstruct sway_seat_device *sway_device) {\n\twlr_cursor_attach_input_device(seat->cursor->cursor,\n\t\tsway_device->input_device->wlr_device);\n}\n\nstatic void seat_configure_tablet_tool(struct sway_seat *seat,\n\t\tstruct sway_seat_device *sway_device) {\n\tif (!sway_device->tablet) {\n\t\tsway_device->tablet = sway_tablet_create(seat, sway_device);\n\t}\n\tsway_configure_tablet(sway_device->tablet);\n\twlr_cursor_attach_input_device(seat->cursor->cursor,\n\t\tsway_device->input_device->wlr_device);\n}\n\nstatic void seat_configure_tablet_pad(struct sway_seat *seat,\n\t\tstruct sway_seat_device *sway_device) {\n\tif (!sway_device->tablet_pad) {\n\t\tsway_device->tablet_pad = sway_tablet_pad_create(seat, sway_device);\n\t}\n\tsway_configure_tablet_pad(sway_device->tablet_pad);\n}\n\nstatic struct sway_seat_device *seat_get_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device) {\n\tstruct sway_seat_device *seat_device = NULL;\n\twl_list_for_each(seat_device, &seat->devices, link) {\n\t\tif (seat_device->input_device == input_device) {\n\t\t\treturn seat_device;\n\t\t}\n\t}\n\n\tstruct sway_keyboard_group *group = NULL;\n\twl_list_for_each(group, &seat->keyboard_groups, link) {\n\t\tif (group->seat_device->input_device == input_device) {\n\t\t\treturn group->seat_device;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nvoid seat_configure_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device) {\n\tstruct sway_seat_device *seat_device = seat_get_device(seat, input_device);\n\tif (!seat_device) {\n\t\treturn;\n\t}\n\n\tswitch (input_device->wlr_device->type) {\n\t\tcase WLR_INPUT_DEVICE_POINTER:\n\t\t\tseat_configure_pointer(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_KEYBOARD:\n\t\t\tseat_configure_keyboard(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_SWITCH:\n\t\t\tseat_configure_switch(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TOUCH:\n\t\t\tseat_configure_touch(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TABLET:\n\t\t\tseat_configure_tablet_tool(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TABLET_PAD:\n\t\t\tseat_configure_tablet_pad(seat, seat_device);\n\t\t\tbreak;\n\t}\n\n\tseat_apply_input_mapping(seat, seat_device);\n}\n\nvoid seat_configure_device_mapping(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device) {\n\tstruct sway_seat_device *seat_device = seat_get_device(seat, input_device);\n\tif (!seat_device) {\n\t\treturn;\n\t}\n\n\tseat_apply_input_mapping(seat, seat_device);\n}\n\nvoid seat_reset_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device) {\n\tstruct sway_seat_device *seat_device = seat_get_device(seat, input_device);\n\tif (!seat_device) {\n\t\treturn;\n\t}\n\n\tswitch (input_device->wlr_device->type) {\n\t\tcase WLR_INPUT_DEVICE_POINTER:\n\t\t\tseat_reset_input_config(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_KEYBOARD:\n\t\t\tsway_keyboard_disarm_key_repeat(seat_device->keyboard);\n\t\t\tsway_keyboard_configure(seat_device->keyboard);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TOUCH:\n\t\t\tseat_reset_input_config(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TABLET:\n\t\t\tseat_reset_input_config(seat, seat_device);\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_TABLET_PAD:\n\t\t\tsway_log(SWAY_DEBUG, \"TODO: reset tablet pad\");\n\t\t\tbreak;\n\t\tcase WLR_INPUT_DEVICE_SWITCH:\n\t\t\tsway_log(SWAY_DEBUG, \"TODO: reset switch device\");\n\t\t\tbreak;\n\t}\n}\n\nvoid seat_add_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device) {\n\tif (seat_get_device(seat, input_device)) {\n\t\tseat_configure_device(seat, input_device);\n\t\treturn;\n\t}\n\n\tstruct sway_seat_device *seat_device =\n\t\tcalloc(1, sizeof(struct sway_seat_device));\n\tif (!seat_device) {\n\t\tsway_log(SWAY_DEBUG, \"could not allocate seat device\");\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"adding device %s to seat %s\",\n\t\tinput_device->identifier, seat->wlr_seat->name);\n\n\tseat_device->sway_seat = seat;\n\tseat_device->input_device = input_device;\n\twl_list_insert(&seat->devices, &seat_device->link);\n\n\tseat_configure_device(seat, input_device);\n\n\tseat_update_capabilities(seat);\n}\n\nvoid seat_remove_device(struct sway_seat *seat,\n\t\tstruct sway_input_device *input_device) {\n\tstruct sway_seat_device *seat_device = seat_get_device(seat, input_device);\n\n\tif (!seat_device) {\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"removing device %s from seat %s\",\n\t\tinput_device->identifier, seat->wlr_seat->name);\n\n\tseat_device_destroy(seat_device);\n\n\tseat_update_capabilities(seat);\n}\n\nstatic bool xcursor_manager_is_named(const struct wlr_xcursor_manager *manager,\n\t\tconst char *name) {\n\treturn (!manager->name && !name) ||\n\t\t(name && manager->name && strcmp(name, manager->name) == 0);\n}\n\nvoid seat_configure_xcursor(struct sway_seat *seat) {\n\tunsigned cursor_size = 24;\n\tconst char *cursor_theme = NULL;\n\n\tconst struct seat_config *seat_config = seat_get_config(seat);\n\tif (!seat_config) {\n\t\tseat_config = seat_get_config_by_name(\"*\");\n\t}\n\tif (seat_config) {\n\t\tcursor_size = seat_config->xcursor_theme.size;\n\t\tcursor_theme = seat_config->xcursor_theme.name;\n\t}\n\n\tif (seat == input_manager_get_default_seat()) {\n\t\tchar cursor_size_fmt[16];\n\t\tsnprintf(cursor_size_fmt, sizeof(cursor_size_fmt), \"%u\", cursor_size);\n\t\tsetenv(\"XCURSOR_SIZE\", cursor_size_fmt, 1);\n\t\tif (cursor_theme != NULL) {\n\t\t\tsetenv(\"XCURSOR_THEME\", cursor_theme, 1);\n\t\t}\n\n#if WLR_HAS_XWAYLAND\n\t\tif (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager ||\n\t\t\t\t!xcursor_manager_is_named(server.xwayland.xcursor_manager,\n\t\t\t\t\tcursor_theme) ||\n\t\t\t\tserver.xwayland.xcursor_manager->size != cursor_size)) {\n\n\t\t\twlr_xcursor_manager_destroy(server.xwayland.xcursor_manager);\n\n\t\t\tserver.xwayland.xcursor_manager =\n\t\t\t\twlr_xcursor_manager_create(cursor_theme, cursor_size);\n\t\t\tsway_assert(server.xwayland.xcursor_manager,\n\t\t\t\t\t\t\"Cannot create XCursor manager for theme\");\n\n\t\t\twlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1);\n\t\t\tstruct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(\n\t\t\t\tserver.xwayland.xcursor_manager, \"default\", 1);\n\t\t\tif (xcursor != NULL) {\n\t\t\t\tstruct wlr_xcursor_image *image = xcursor->images[0];\n\t\t\t\tstruct wlr_buffer *buffer = wlr_xcursor_image_get_buffer(image);\n\t\t\t\twlr_xwayland_set_cursor(\n\t\t\t\t\tserver.xwayland.wlr_xwayland, buffer,\n\t\t\t\t\timage->hotspot_x, image->hotspot_y);\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\t/* Create xcursor manager if we don't have one already, or if the\n\t * theme has changed */\n\tif (!seat->cursor->xcursor_manager ||\n\t\t\t!xcursor_manager_is_named(\n\t\t\t\tseat->cursor->xcursor_manager, cursor_theme) ||\n\t\t\tseat->cursor->xcursor_manager->size != cursor_size) {\n\n\t\twlr_xcursor_manager_destroy(seat->cursor->xcursor_manager);\n\t\tseat->cursor->xcursor_manager =\n\t\t\twlr_xcursor_manager_create(cursor_theme, cursor_size);\n\t\tif (!seat->cursor->xcursor_manager) {\n\t\t\tsway_log(SWAY_ERROR,\n\t\t\t\t\"Cannot create XCursor manager for theme '%s'\", cursor_theme);\n\t\t}\n\n\n\t\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\t\tstruct sway_output *sway_output = root->outputs->items[i];\n\t\t\tstruct wlr_output *output = sway_output->wlr_output;\n\t\t\tbool result =\n\t\t\t\twlr_xcursor_manager_load(seat->cursor->xcursor_manager,\n\t\t\t\t\toutput->scale);\n\t\t\tif (!result) {\n\t\t\t\tsway_log(SWAY_ERROR,\n\t\t\t\t\t\"Cannot load xcursor theme for output '%s' with scale %f\",\n\t\t\t\t\toutput->name, output->scale);\n\t\t\t}\n\t\t}\n\n\t\t// Reset the cursor so that we apply it to outputs that just appeared\n\t\tcursor_set_image(seat->cursor, NULL, NULL);\n\t\tcursor_set_image(seat->cursor, \"default\", NULL);\n\t\twlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,\n\t\t\tseat->cursor->cursor->y);\n\t}\n}\n\nbool seat_is_input_allowed(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface) {\n\tif (server.session_lock.lock) {\n\t\treturn sway_session_lock_has_surface(server.session_lock.lock, surface);\n\t}\n\treturn true;\n}\n\nstatic void send_unfocus(struct sway_container *con, void *data) {\n\tif (con->view) {\n\t\tview_set_activated(con->view, false);\n\t}\n}\n\n// Unfocus the container and any children (eg. when leaving `focus parent`)\nstatic void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) {\n\tsway_cursor_constrain(seat->cursor, NULL);\n\twlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);\n\tif (node->type == N_WORKSPACE) {\n\t\tworkspace_for_each_container(node->sway_workspace, send_unfocus, seat);\n\t} else {\n\t\tsend_unfocus(node->sway_container, seat);\n\t\tcontainer_for_each_child(node->sway_container, send_unfocus, seat);\n\t}\n}\n\nstatic int handle_urgent_timeout(void *data) {\n\tstruct sway_view *view = data;\n\tview_set_urgent(view, false);\n\tcontainer_update_itself_and_parents(view->container);\n\treturn 0;\n}\n\nstatic void set_workspace(struct sway_seat *seat,\n\t\tstruct sway_workspace *new_ws) {\n\tif (seat->workspace == new_ws) {\n\t\treturn;\n\t}\n\n\tif (seat->workspace) {\n\t\tfree(seat->prev_workspace_name);\n\t\tseat->prev_workspace_name = strdup(seat->workspace->name);\n\t\tif (!seat->prev_workspace_name) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to allocate previous workspace name\");\n\t\t}\n\t}\n\n\tipc_event_workspace(seat->workspace, new_ws, \"focus\");\n\tseat->workspace = new_ws;\n}\n\nvoid seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {\n\tstruct sway_seat_node *seat_node = seat_node_from_node(seat, node);\n\twl_list_remove(&seat_node->link);\n\twl_list_insert(&seat->focus_stack, &seat_node->link);\n\tnode_set_dirty(node);\n\n\t// If focusing a scratchpad container that is fullscreen global, parent\n\t// will be NULL\n\tstruct sway_node *parent = node_get_parent(node);\n\tif (parent) {\n\t\tnode_set_dirty(parent);\n\t}\n}\n\nstatic void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) {\n\tstruct sway_node *last_focus = seat_get_focus(seat);\n\tif (last_focus == node) {\n\t\treturn;\n\t}\n\n\tstruct sway_workspace *last_workspace = seat_get_focused_workspace(seat);\n\n\tif (node == NULL) {\n\t\t// Close any popups on the old focus\n\t\tif (node_is_view(last_focus)) {\n\t\t\tview_close_popups(last_focus->sway_container->view);\n\t\t}\n\t\tseat_send_unfocus(last_focus, seat);\n\t\tsway_input_method_relay_set_focus(&seat->im_relay, NULL);\n\t\tseat->has_focus = false;\n\t\treturn;\n\t}\n\n\tstruct sway_workspace *new_workspace = node->type == N_WORKSPACE ?\n\t\tnode->sway_workspace : node->sway_container->pending.workspace;\n\tstruct sway_container *container = node->type == N_CONTAINER ?\n\t\tnode->sway_container : NULL;\n\n\t// Deny setting focus to a view which is hidden by a fullscreen container or global\n\tif (container && container_obstructing_fullscreen_container(container)) {\n\t\treturn;\n\t}\n\n\t// Deny setting focus to a workspace node when using fullscreen global\n\tif (root->fullscreen_global && !container && new_workspace) {\n\t\treturn;\n\t}\n\n\tstruct sway_output *new_output =\n\t\tnew_workspace ? new_workspace->output : NULL;\n\n\tif (last_workspace != new_workspace && new_output) {\n\t\tnode_set_dirty(&new_output->node);\n\t}\n\n\t// find new output's old workspace, which might have to be removed if empty\n\tstruct sway_workspace *new_output_last_ws =\n\t\tnew_output ? output_get_active_workspace(new_output) : NULL;\n\n\t// Unfocus the previous focus\n\tif (last_focus) {\n\t\tseat_send_unfocus(last_focus, seat);\n\t\tnode_set_dirty(last_focus);\n\t\tstruct sway_node *parent = node_get_parent(last_focus);\n\t\tif (parent) {\n\t\t\tnode_set_dirty(parent);\n\t\t}\n\t}\n\n\t// Put the container parents on the focus stack, then the workspace, then\n\t// the focused container.\n\tif (container) {\n\t\tstruct sway_container *parent = container->pending.parent;\n\t\twhile (parent) {\n\t\t\tseat_set_raw_focus(seat, &parent->node);\n\t\t\tparent = parent->pending.parent;\n\t\t}\n\t}\n\tif (new_workspace) {\n\t\tseat_set_raw_focus(seat, &new_workspace->node);\n\t}\n\tif (container) {\n\t\tseat_set_raw_focus(seat, &container->node);\n\t\tseat_send_focus(&container->node, seat);\n\t}\n\n\t// emit ipc events\n\tset_workspace(seat, new_workspace);\n\tif (container && container->view) {\n\t\tipc_event_window(container, \"focus\");\n\t}\n\n\t// Move sticky containers to new workspace\n\tif (new_workspace && new_output_last_ws\n\t\t\t&& new_workspace != new_output_last_ws) {\n\t\tfor (int i = 0; i < new_output_last_ws->floating->length; ++i) {\n\t\t\tstruct sway_container *floater =\n\t\t\t\tnew_output_last_ws->floating->items[i];\n\t\t\tif (container_is_sticky(floater)) {\n\t\t\t\tcontainer_detach(floater);\n\t\t\t\tworkspace_add_floating(new_workspace, floater);\n\t\t\t\t--i;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Close any popups on the old focus\n\tif (last_focus && node_is_view(last_focus)) {\n\t\tview_close_popups(last_focus->sway_container->view);\n\t}\n\n\t// If urgent, either unset the urgency or start a timer to unset it\n\tif (container && container->view && view_is_urgent(container->view) &&\n\t\t\t!container->view->urgent_timer) {\n\t\tstruct sway_view *view = container->view;\n\t\tif (last_workspace && last_workspace != new_workspace &&\n\t\t\t\tconfig->urgent_timeout > 0) {\n\t\t\tview->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop,\n\t\t\t\t\thandle_urgent_timeout, view);\n\t\t\tif (view->urgent_timer) {\n\t\t\t\twl_event_source_timer_update(view->urgent_timer,\n\t\t\t\t\t\tconfig->urgent_timeout);\n\t\t\t} else {\n\t\t\t\tsway_log_errno(SWAY_ERROR, \"Unable to create urgency timer\");\n\t\t\t\thandle_urgent_timeout(view);\n\t\t\t}\n\t\t} else {\n\t\t\tview_set_urgent(view, false);\n\t\t}\n\t}\n\n\tif (new_output_last_ws) {\n\t\tworkspace_consider_destroy(new_output_last_ws);\n\t}\n\tif (last_workspace && last_workspace != new_output_last_ws) {\n\t\tworkspace_consider_destroy(last_workspace);\n\t}\n\n\tseat->has_focus = true;\n\n\tif (config->smart_gaps && new_workspace) {\n\t\t// When smart gaps is on, gaps may change when the focus changes so\n\t\t// the workspace needs to be arranged\n\t\tarrange_workspace(new_workspace);\n\t}\n}\n\nvoid seat_set_focus(struct sway_seat *seat, struct sway_node *node) {\n\t// Prevents the layer from losing focus if it has keyboard exclusivity\n\tif (seat->has_exclusive_layer) {\n\t\tstruct wlr_layer_surface_v1 *layer = seat->focused_layer;\n\t\tseat_set_focus_layer(seat, NULL);\n\t\tseat_set_workspace_focus(seat, node);\n\t\tseat_set_focus_layer(seat, layer);\n\t} else if (seat->focused_layer) {\n\t\tseat_set_focus_layer(seat, NULL);\n\t\tseat_set_workspace_focus(seat, node);\n\t} else {\n\t\tseat_set_workspace_focus(seat, node);\n\t}\n\tif (server.session_lock.lock) {\n\t\tseat_set_focus_surface(seat, server.session_lock.lock->focused, false);\n\t}\n}\n\nvoid seat_set_focus_container(struct sway_seat *seat,\n\t\tstruct sway_container *con) {\n\tseat_set_focus(seat, con ? &con->node : NULL);\n}\n\nvoid seat_set_focus_workspace(struct sway_seat *seat,\n\t\tstruct sway_workspace *ws) {\n\tseat_set_focus(seat, ws ? &ws->node : NULL);\n}\n\nvoid seat_set_focus_surface(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface, bool unfocus) {\n\tif (seat->has_focus && unfocus) {\n\t\tstruct sway_node *focus = seat_get_focus(seat);\n\t\tseat_send_unfocus(focus, seat);\n\t\tseat->has_focus = false;\n\t}\n\n\tif (surface) {\n\t\tseat_keyboard_notify_enter(seat, surface);\n\t} else {\n\t\twlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);\n\t}\n\n\tsway_input_method_relay_set_focus(&seat->im_relay, surface);\n\tseat_tablet_pads_set_focus(seat, surface);\n}\n\nvoid seat_set_focus_layer(struct sway_seat *seat,\n\t\tstruct wlr_layer_surface_v1 *layer) {\n\tif (!layer && seat->focused_layer) {\n\t\tseat->focused_layer = NULL;\n\t\tstruct sway_node *previous = seat_get_focus_inactive(seat, &root->node);\n\t\tif (previous) {\n\t\t\t// Hack to get seat to re-focus the return value of get_focus\n\t\t\tseat_set_focus(seat, NULL);\n\t\t\tseat_set_focus(seat, previous);\n\t\t}\n\t\treturn;\n\t} else if (!layer) {\n\t\treturn;\n\t}\n\tassert(layer->surface->mapped);\n\tif (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP &&\n\t\t\tlayer->current.keyboard_interactive\n\t\t\t== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {\n\t\tseat->has_exclusive_layer = true;\n\t}\n\tif (seat->focused_layer == layer) {\n\t\treturn;\n\t}\n\tseat_set_focus_surface(seat, layer->surface, true);\n\tseat->focused_layer = layer;\n}\n\nvoid seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) {\n\tif (seat->focused_layer) {\n\t\tif (wl_resource_get_client(seat->focused_layer->resource) != client) {\n\t\t\tseat_set_focus_layer(seat, NULL);\n\t\t}\n\t}\n\tif (seat->has_focus) {\n\t\tstruct sway_node *focus = seat_get_focus(seat);\n\t\tif (node_is_view(focus) && wl_resource_get_client(\n\t\t\t\t\tfocus->sway_container->view->surface->resource) != client) {\n\t\t\tseat_set_focus(seat, NULL);\n\t\t}\n\t}\n\tif (seat->wlr_seat->pointer_state.focused_client) {\n\t\tif (seat->wlr_seat->pointer_state.focused_client->client != client) {\n\t\t\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n\t\t}\n\t}\n\tstruct timespec now;\n\tclock_gettime(CLOCK_MONOTONIC, &now);\n\tstruct wlr_touch_point *point;\n\twl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) {\n\t\tif (point->client->client != client) {\n\t\t\twlr_seat_touch_point_clear_focus(seat->wlr_seat,\n\t\t\t\t\tnow.tv_nsec / 1000, point->touch_id);\n\t\t}\n\t}\n}\n\nstruct sway_node *seat_get_focus_inactive(struct sway_seat *seat,\n\t\tstruct sway_node *node) {\n\tif (node_is_view(node)) {\n\t\treturn node;\n\t}\n\tstruct sway_seat_node *current;\n\twl_list_for_each(current, &seat->focus_stack, link) {\n\t\tif (node_has_ancestor(current->node, node)) {\n\t\t\treturn current->node;\n\t\t}\n\t}\n\tif (node->type == N_WORKSPACE) {\n\t\treturn node;\n\t}\n\treturn NULL;\n}\n\nstruct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,\n\t\tstruct sway_workspace *workspace) {\n\tif (!workspace->tiling->length) {\n\t\treturn NULL;\n\t}\n\tstruct sway_seat_node *current;\n\twl_list_for_each(current, &seat->focus_stack, link) {\n\t\tstruct sway_node *node = current->node;\n\t\tif (node->type == N_CONTAINER &&\n\t\t\t\t!container_is_floating_or_child(node->sway_container) &&\n\t\t\t\tnode->sway_container->pending.workspace == workspace) {\n\t\t\treturn node->sway_container;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,\n\t\tstruct sway_workspace *workspace) {\n\tif (!workspace->floating->length) {\n\t\treturn NULL;\n\t}\n\tstruct sway_seat_node *current;\n\twl_list_for_each(current, &seat->focus_stack, link) {\n\t\tstruct sway_node *node = current->node;\n\t\tif (node->type == N_CONTAINER &&\n\t\t\t\tcontainer_is_floating_or_child(node->sway_container) &&\n\t\t\t\tnode->sway_container->pending.workspace == workspace) {\n\t\t\treturn node->sway_container;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_node *seat_get_active_tiling_child(struct sway_seat *seat,\n\t\tstruct sway_node *parent) {\n\tif (node_is_view(parent)) {\n\t\treturn parent;\n\t}\n\tstruct sway_seat_node *current;\n\twl_list_for_each(current, &seat->focus_stack, link) {\n\t\tstruct sway_node *node = current->node;\n\t\tif (node_get_parent(node) != parent) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (parent->type == N_WORKSPACE) {\n\t\t\t// Only consider tiling children\n\t\t\tstruct sway_workspace *ws = parent->sway_workspace;\n\t\t\tif (list_find(ws->tiling, node->sway_container) == -1) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\treturn node;\n\t}\n\treturn NULL;\n}\n\nstruct sway_node *seat_get_focus(struct sway_seat *seat) {\n\tif (!seat->has_focus) {\n\t\treturn NULL;\n\t}\n\tsway_assert(!wl_list_empty(&seat->focus_stack),\n\t\t\t\"focus_stack is empty, but has_focus is true\");\n\tstruct sway_seat_node *current =\n\t\twl_container_of(seat->focus_stack.next, current, link);\n\treturn current->node;\n}\n\nstruct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) {\n\tstruct sway_node *focus = seat_get_focus_inactive(seat, &root->node);\n\tif (!focus) {\n\t\treturn NULL;\n\t}\n\tif (focus->type == N_CONTAINER) {\n\t\treturn focus->sway_container->pending.workspace;\n\t}\n\tif (focus->type == N_WORKSPACE) {\n\t\treturn focus->sway_workspace;\n\t}\n\treturn NULL; // output doesn't have a workspace yet\n}\n\nstruct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) {\n\tstruct sway_seat_node *current;\n\twl_list_for_each(current, &seat->focus_stack, link) {\n\t\tstruct sway_node *node = current->node;\n\t\tif (node->type == N_CONTAINER &&\n\t\t\t\tnode->sway_container->pending.workspace) {\n\t\t\treturn node->sway_container->pending.workspace;\n\t\t} else if (node->type == N_WORKSPACE) {\n\t\t\treturn node->sway_workspace;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_container *seat_get_focused_container(struct sway_seat *seat) {\n\tstruct sway_node *focus = seat_get_focus(seat);\n\tif (focus && focus->type == N_CONTAINER) {\n\t\treturn focus->sway_container;\n\t}\n\treturn NULL;\n}\n\nvoid seat_apply_config(struct sway_seat *seat,\n\t\tstruct seat_config *seat_config) {\n\tstruct sway_seat_device *seat_device = NULL;\n\n\tif (!seat_config) {\n\t\treturn;\n\t}\n\n\tseat->idle_inhibit_sources = seat_config->idle_inhibit_sources;\n\tseat->idle_wake_sources = seat_config->idle_wake_sources;\n\n\twl_list_for_each(seat_device, &seat->devices, link) {\n\t\tseat_configure_device(seat, seat_device->input_device);\n\t\tcursor_handle_activity_from_device(seat->cursor,\n\t\t\tseat_device->input_device->wlr_device);\n\t}\n}\n\nstruct seat_config *seat_get_config(struct sway_seat *seat) {\n\tstruct seat_config *seat_config = NULL;\n\tfor (int i = 0; i < config->seat_configs->length; ++i ) {\n\t\tseat_config = config->seat_configs->items[i];\n\t\tif (strcmp(seat->wlr_seat->name, seat_config->name) == 0) {\n\t\t\treturn seat_config;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nstruct seat_config *seat_get_config_by_name(const char *name) {\n\tstruct seat_config *seat_config = NULL;\n\tfor (int i = 0; i < config->seat_configs->length; ++i ) {\n\t\tseat_config = config->seat_configs->items[i];\n\t\tif (strcmp(name, seat_config->name) == 0) {\n\t\t\treturn seat_config;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nvoid seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tuint32_t button, enum wl_pointer_button_state state) {\n\tseat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat,\n\t\t\ttime_msec, button, state);\n}\n\nvoid seat_consider_warp_to_focus(struct sway_seat *seat) {\n\tstruct sway_node *focus = seat_get_focus(seat);\n\tif (config->mouse_warping == WARP_NO || !focus) {\n\t\treturn;\n\t}\n\tif (config->mouse_warping == WARP_OUTPUT) {\n\t\tstruct sway_output *output = node_get_output(focus);\n\t\tif (output) {\n\t\t\tstruct wlr_box box;\n\t\t\toutput_get_box(output, &box);\n\t\t\tif (wlr_box_contains_point(&box,\n\t\t\t\t\t\tseat->cursor->cursor->x, seat->cursor->cursor->y)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (focus->type == N_CONTAINER) {\n\t\tcursor_warp_to_container(seat->cursor, focus->sway_container, false);\n\t} else {\n\t\tcursor_warp_to_workspace(seat->cursor, focus->sway_workspace);\n\t}\n}\n\nvoid seatop_unref(struct sway_seat *seat, struct sway_container *con) {\n\tif (seat->seatop_impl->unref) {\n\t\tseat->seatop_impl->unref(seat, con);\n\t}\n}\n\nvoid seatop_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tif (seat->seatop_impl->button) {\n\t\tseat->seatop_impl->button(seat, time_msec, device, button, state);\n\t}\n}\n\nvoid seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {\n\tif (seat->seatop_impl->pointer_motion) {\n\t\tseat->seatop_impl->pointer_motion(seat, time_msec);\n\t}\n}\n\nvoid seatop_pointer_axis(struct sway_seat *seat,\n\t\tstruct wlr_pointer_axis_event *event) {\n\tif (seat->seatop_impl->pointer_axis) {\n\t\tseat->seatop_impl->pointer_axis(seat, event);\n\t}\n}\n\nvoid seatop_touch_motion(struct sway_seat *seat, struct wlr_touch_motion_event *event,\n\t\tdouble lx, double ly) {\n\tif (seat->seatop_impl->touch_motion) {\n\t\tseat->seatop_impl->touch_motion(seat, event, lx, ly);\n\t}\n}\n\nvoid seatop_touch_up(struct sway_seat *seat, struct wlr_touch_up_event *event) {\n\tif (seat->seatop_impl->touch_up) {\n\t\tseat->seatop_impl->touch_up(seat, event);\n\t}\n}\n\nvoid seatop_touch_down(struct sway_seat *seat, struct wlr_touch_down_event *event,\n\t\tdouble lx, double ly) {\n\tif (seat->seatop_impl->touch_down) {\n\t\tseat->seatop_impl->touch_down(seat, event, lx, ly);\n\t}\n}\n\nvoid seatop_touch_cancel(struct sway_seat *seat, struct wlr_touch_cancel_event *event) {\n\tif (seat->seatop_impl->touch_cancel) {\n\t\tseat->seatop_impl->touch_cancel(seat, event);\n\t}\n}\n\nvoid seatop_tablet_tool_tip(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec,\n\t\tenum wlr_tablet_tool_tip_state state) {\n\tif (seat->seatop_impl->tablet_tool_tip) {\n\t\tseat->seatop_impl->tablet_tool_tip(seat, tool, time_msec, state);\n\t}\n}\n\nvoid seatop_tablet_tool_motion(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec) {\n\tif (seat->seatop_impl->tablet_tool_motion) {\n\t\tseat->seatop_impl->tablet_tool_motion(seat, tool, time_msec);\n\t} else {\n\t\tseatop_pointer_motion(seat, time_msec);\n\t}\n}\n\nvoid seatop_hold_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_hold_begin_event *event) {\n\tif (seat->seatop_impl->hold_begin) {\n\t\tseat->seatop_impl->hold_begin(seat, event);\n\t}\n}\n\nvoid seatop_hold_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_hold_end_event *event) {\n\tif (seat->seatop_impl->hold_end) {\n\t\tseat->seatop_impl->hold_end(seat, event);\n\t}\n}\n\nvoid seatop_pinch_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_begin_event *event) {\n\tif (seat->seatop_impl->pinch_begin) {\n\t\tseat->seatop_impl->pinch_begin(seat, event);\n\t}\n}\n\nvoid seatop_pinch_update(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_update_event *event) {\n\tif (seat->seatop_impl->pinch_update) {\n\t\tseat->seatop_impl->pinch_update(seat, event);\n\t}\n}\n\nvoid seatop_pinch_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_end_event *event) {\n\tif (seat->seatop_impl->pinch_end) {\n\t\tseat->seatop_impl->pinch_end(seat, event);\n\t}\n}\n\nvoid seatop_swipe_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_begin_event *event) {\n\tif (seat->seatop_impl->swipe_begin) {\n\t\tseat->seatop_impl->swipe_begin(seat, event);\n\t}\n}\n\nvoid seatop_swipe_update(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_update_event *event) {\n\tif (seat->seatop_impl->swipe_update) {\n\t\tseat->seatop_impl->swipe_update(seat, event);\n\t}\n}\n\nvoid seatop_swipe_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_end_event *event) {\n\tif (seat->seatop_impl->swipe_end) {\n\t\tseat->seatop_impl->swipe_end(seat, event);\n\t}\n}\n\nvoid seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {\n\tif (seat->seatop_impl->rebase) {\n\t\tseat->seatop_impl->rebase(seat, time_msec);\n\t}\n}\n\nvoid seatop_end(struct sway_seat *seat) {\n\tif (seat->seatop_impl && seat->seatop_impl->end) {\n\t\tseat->seatop_impl->end(seat);\n\t}\n\tfree(seat->seatop_data);\n\tseat->seatop_data = NULL;\n\tseat->seatop_impl = NULL;\n}\n\nbool seatop_allows_set_cursor(struct sway_seat *seat) {\n\treturn seat->seatop_impl->allow_set_cursor;\n}\n\nstruct sway_keyboard_shortcuts_inhibitor *\nkeyboard_shortcuts_inhibitor_get_for_surface(\n\t\tconst struct sway_seat *seat,\n\t\tconst struct wlr_surface *surface) {\n\tstruct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = NULL;\n\twl_list_for_each(sway_inhibitor, &seat->keyboard_shortcuts_inhibitors, link) {\n\t\tif (sway_inhibitor->inhibitor->surface == surface) {\n\t\t\treturn sway_inhibitor;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nstruct sway_keyboard_shortcuts_inhibitor *\nkeyboard_shortcuts_inhibitor_get_for_focused_surface(\n\t\tconst struct sway_seat *seat) {\n\treturn keyboard_shortcuts_inhibitor_get_for_surface(seat,\n\t\tseat->wlr_seat->keyboard_state.focused_surface);\n}\n"
  },
  {
    "path": "sway/input/seatop_default.c",
    "content": "#include <float.h>\n#include <libevdev/libevdev.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_subcompositor.h>\n#include <wlr/types/wlr_tablet_v2.h>\n#include <wlr/types/wlr_xcursor_manager.h>\n#include \"gesture.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/input/tablet.h\"\n#include \"sway/layers.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"log.h\"\n#include \"util.h\"\n#if WLR_HAS_XWAYLAND\n#include \"sway/xwayland.h\"\n#endif\n\nstruct seatop_default_event {\n\tstruct sway_node *previous_node;\n\tuint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];\n\tsize_t pressed_button_count;\n\tstruct gesture_tracker gestures;\n};\n\n/*-----------------------------------------\\\n * Functions shared by multiple callbacks  /\n *---------------------------------------*/\n\n/**\n * Determine if the edge of the given container is on the edge of the\n * workspace/output.\n */\nstatic bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {\n\tenum sway_container_layout layout = L_NONE;\n\tswitch (edge) {\n\tcase WLR_EDGE_TOP:\n\tcase WLR_EDGE_BOTTOM:\n\t\tlayout = L_VERT;\n\t\tbreak;\n\tcase WLR_EDGE_LEFT:\n\tcase WLR_EDGE_RIGHT:\n\t\tlayout = L_HORIZ;\n\t\tbreak;\n\tcase WLR_EDGE_NONE:\n\t\tsway_assert(false, \"Never reached\");\n\t\treturn false;\n\t}\n\n\t// Iterate the parents until we find one with the layout we want,\n\t// then check if the child has siblings between it and the edge.\n\twhile (cont) {\n\t\tif (container_parent_layout(cont) == layout) {\n\t\t\tlist_t *siblings = container_get_siblings(cont);\n\t\t\tif (!siblings) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tint index = list_find(siblings, cont);\n\t\t\tif (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (index < siblings->length - 1 &&\n\t\t\t\t\t(edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tcont = cont->pending.parent;\n\t}\n\treturn true;\n}\n\nstatic enum wlr_edges find_edge(struct sway_container *cont,\n\t\tstruct wlr_surface *surface, struct sway_cursor *cursor) {\n\tif (!cont->view || (surface && cont->view->surface != surface)) {\n\t\treturn WLR_EDGE_NONE;\n\t}\n\tif (cont->pending.border == B_NONE || !cont->pending.border_thickness ||\n\t\t\tcont->pending.border == B_CSD) {\n\t\treturn WLR_EDGE_NONE;\n\t}\n\tif (cont->pending.fullscreen_mode) {\n\t\treturn WLR_EDGE_NONE;\n\t}\n\n\tenum wlr_edges edge = 0;\n\tif (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) {\n\t\tedge |= WLR_EDGE_LEFT;\n\t}\n\tif (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) {\n\t\tedge |= WLR_EDGE_TOP;\n\t}\n\tif (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) {\n\t\tedge |= WLR_EDGE_RIGHT;\n\t}\n\tif (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.border_thickness) {\n\t\tedge |= WLR_EDGE_BOTTOM;\n\t}\n\n\treturn edge;\n}\n\n/**\n * If the cursor is over a _resizable_ edge, return the edge.\n * Edges that can't be resized are edges of the workspace.\n */\nenum wlr_edges find_resize_edge(struct sway_container *cont,\n\t\tstruct wlr_surface *surface, struct sway_cursor *cursor) {\n\tenum wlr_edges edge = find_edge(cont, surface, cursor);\n\tif (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) {\n\t\treturn WLR_EDGE_NONE;\n\t}\n\treturn edge;\n}\n\n/**\n * Return the mouse binding which matches modifier, click location, release,\n * and pressed button state, otherwise return null.\n */\nstatic struct sway_binding* get_active_mouse_binding(\n\t\tstruct seatop_default_event *e, list_t *bindings, uint32_t modifiers,\n\t\tbool release, bool on_titlebar, bool on_border, bool on_content,\n\t\tbool on_workspace, const char *identifier) {\n\tuint32_t click_region =\n\t\t\t((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) |\n\t\t\t((on_border || on_workspace) ? BINDING_BORDER : 0) |\n\t\t\t((on_content || on_workspace) ? BINDING_CONTENTS : 0);\n\n\tstruct sway_binding *current = NULL;\n\tfor (int i = 0; i < bindings->length; ++i) {\n\t\tstruct sway_binding *binding = bindings->items[i];\n\t\tif (modifiers ^ binding->modifiers ||\n\t\t\t\te->pressed_button_count != (size_t)binding->keys->length ||\n\t\t\t\trelease != (binding->flags & BINDING_RELEASE) ||\n\t\t\t\t!(click_region & binding->flags) ||\n\t\t\t\t(on_workspace &&\n\t\t\t\t (click_region & binding->flags) != click_region) ||\n\t\t\t\t(strcmp(binding->input, identifier) != 0 &&\n\t\t\t\t strcmp(binding->input, \"*\") != 0)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool match = true;\n\t\tfor (size_t j = 0; j < e->pressed_button_count; j++) {\n\t\t\tuint32_t key = *(uint32_t *)binding->keys->items[j];\n\t\t\tif (key != e->pressed_buttons[j]) {\n\t\t\t\tmatch = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!match) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!current || strcmp(current->input, \"*\") == 0) {\n\t\t\tcurrent = binding;\n\t\t\tif (strcmp(current->input, identifier) == 0) {\n\t\t\t\t// If a binding is found for the exact input, quit searching\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn current;\n}\n\n/**\n * Remove a button (and duplicates) from the sorted list of currently pressed\n * buttons.\n */\nstatic void state_erase_button(struct seatop_default_event *e,\n\t\tuint32_t button) {\n\tsize_t j = 0;\n\tfor (size_t i = 0; i < e->pressed_button_count; ++i) {\n\t\tif (i > j) {\n\t\t\te->pressed_buttons[j] = e->pressed_buttons[i];\n\t\t}\n\t\tif (e->pressed_buttons[i] != button) {\n\t\t\t++j;\n\t\t}\n\t}\n\twhile (e->pressed_button_count > j) {\n\t\t--e->pressed_button_count;\n\t\te->pressed_buttons[e->pressed_button_count] = 0;\n\t}\n}\n\n/**\n * Add a button to the sorted list of currently pressed buttons, if there\n * is space.\n */\nstatic void state_add_button(struct seatop_default_event *e, uint32_t button) {\n\tif (e->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) {\n\t\treturn;\n\t}\n\tsize_t i = 0;\n\twhile (i < e->pressed_button_count && e->pressed_buttons[i] < button) {\n\t\t++i;\n\t}\n\tsize_t j = e->pressed_button_count;\n\twhile (j > i) {\n\t\te->pressed_buttons[j] = e->pressed_buttons[j - 1];\n\t\t--j;\n\t}\n\te->pressed_buttons[i] = button;\n\te->pressed_button_count++;\n}\n\n/*-------------------------------------------\\\n * Functions used by handle_tablet_tool_tip  /\n *-----------------------------------------*/\n\nstatic void handle_tablet_tool_tip(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec,\n\t\tenum wlr_tablet_tool_tip_state state) {\n\tif (state == WLR_TABLET_TOOL_TIP_UP) {\n\t\twlr_tablet_v2_tablet_tool_notify_up(tool->tablet_v2_tool);\n\t\treturn;\n\t}\n\n\tstruct sway_cursor *cursor = seat->cursor;\n\tstruct wlr_surface *surface = NULL;\n\tdouble sx, sy;\n\tstruct sway_node *node = node_at_coords(seat,\n\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\tif (!sway_assert(surface,\n\t\t\t\"Expected null-surface tablet input to route through pointer emulation\")) {\n\t\treturn;\n\t}\n\n\tstruct sway_container *cont = node && node->type == N_CONTAINER ?\n\t\tnode->sway_container : NULL;\n\n\tstruct wlr_layer_surface_v1 *layer;\n#if WLR_HAS_XWAYLAND\n\tstruct wlr_xwayland_surface *xsurface;\n#endif\n\tif ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) &&\n\t\t\tlayer->current.keyboard_interactive) {\n\t\t// Handle tapping a layer surface\n\t\tseat_set_focus_layer(seat, layer);\n\t\ttransaction_commit_dirty();\n\t} else if (cont) {\n\t\tbool is_floating_or_child = container_is_floating_or_child(cont);\n\t\tbool is_fullscreen_or_child = container_is_fullscreen_or_child(cont);\n\t\tstruct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);\n\t\tbool mod_pressed = keyboard &&\n\t\t\t(wlr_keyboard_get_modifiers(keyboard) & config->floating_mod);\n\n\t\t// Handle beginning floating move\n\t\tif (is_floating_or_child && !is_fullscreen_or_child && mod_pressed) {\n\t\t\tseat_set_focus_container(seat,\n\t\t\t\tseat_get_focus_inactive_view(seat, &cont->node));\n\t\t\tseatop_begin_move_floating(seat, container_toplevel_ancestor(cont));\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle moving a tiling container\n\t\tif (config->tiling_drag && mod_pressed && !is_floating_or_child &&\n\t\t\t\tcont->pending.fullscreen_mode == FULLSCREEN_NONE) {\n\t\t\tseatop_begin_move_tiling(seat, cont);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle tapping on a container surface\n\t\tseat_set_focus_container(seat, cont);\n\t\tseatop_begin_down(seat, node->sway_container, sx, sy);\n\t}\n#if WLR_HAS_XWAYLAND\n\t// Handle tapping on an xwayland unmanaged view\n\telse if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&\n\t\t\txsurface->override_redirect &&\n\t\t\twlr_xwayland_surface_override_redirect_wants_focus(xsurface)) {\n\t\tstruct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;\n\t\twlr_xwayland_set_seat(xwayland, seat->wlr_seat);\n\t\tseat_set_focus_surface(seat, xsurface->surface, false);\n\t\ttransaction_commit_dirty();\n\t}\n#endif\n\n\twlr_tablet_v2_tablet_tool_notify_down(tool->tablet_v2_tool);\n\twlr_tablet_tool_v2_start_implicit_grab(tool->tablet_v2_tool);\n}\n\n/*----------------------------------\\\n * Functions used by handle_button  /\n *--------------------------------*/\n\nstatic bool trigger_pointer_button_binding(struct sway_seat *seat,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state, uint32_t modifiers,\n\t\tbool on_titlebar, bool on_border, bool on_contents, bool on_workspace) {\n\t// We can reach this for non-pointer devices if we're currently emulating\n\t// pointer input for one. Emulated input should not trigger bindings. The\n\t// device can be NULL if this is synthetic (e.g. swaymsg-generated) input.\n\tif (device && device->type != WLR_INPUT_DEVICE_POINTER) {\n\t\treturn false;\n\t}\n\n\tstruct seatop_default_event *e = seat->seatop_data;\n\n\tchar *device_identifier = device ? input_device_get_identifier(device)\n\t\t: strdup(\"*\");\n\tstruct sway_binding *binding = NULL;\n\tif (state == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\tstate_add_button(e, button);\n\t\tbinding = get_active_mouse_binding(e,\n\t\t\tconfig->current_mode->mouse_bindings, modifiers, false,\n\t\t\ton_titlebar, on_border, on_contents, on_workspace,\n\t\t\tdevice_identifier);\n\t} else {\n\t\tbinding = get_active_mouse_binding(e,\n\t\t\tconfig->current_mode->mouse_bindings, modifiers, true,\n\t\t\ton_titlebar, on_border, on_contents, on_workspace,\n\t\t\tdevice_identifier);\n\t\tstate_erase_button(e, button);\n\t}\n\n\tfree(device_identifier);\n\tif (binding) {\n\t\tseat_execute_command(seat, binding);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic void handle_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tstruct sway_cursor *cursor = seat->cursor;\n\n\t// Determine what's under the cursor\n\tstruct wlr_surface *surface = NULL;\n\tdouble sx, sy;\n\tstruct sway_node *node = node_at_coords(seat,\n\t\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\tstruct sway_container *cont = node && node->type == N_CONTAINER ?\n\t\tnode->sway_container : NULL;\n\tbool is_floating = cont && container_is_floating(cont);\n\tbool is_floating_or_child = cont && container_is_floating_or_child(cont);\n\tbool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont);\n\tenum wlr_edges edge = cont ? find_edge(cont, surface, cursor) : WLR_EDGE_NONE;\n\tenum wlr_edges resize_edge = cont && edge ?\n\t\tfind_resize_edge(cont, surface, cursor) : WLR_EDGE_NONE;\n\tbool on_border = edge != WLR_EDGE_NONE;\n\tbool on_contents = cont && !on_border && surface;\n\tbool on_workspace = node && node->type == N_WORKSPACE;\n\tbool on_titlebar = cont && !on_border && !surface;\n\n\tstruct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);\n\tuint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;\n\n\tbool mod_pressed = modifiers & config->floating_mod;\n\tuint32_t mod_move_btn = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;\n\tuint32_t mod_resize_btn = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT;\n\tbool mod_move_btn_pressed = mod_pressed && button == mod_move_btn;\n\tbool mod_resize_btn_pressed = mod_pressed && button == mod_resize_btn;\n\tbool titlebar_left_btn_pressed = on_titlebar && button == BTN_LEFT;\n\n\t// Handle mouse bindings\n\tif (trigger_pointer_button_binding(seat, device, button, state, modifiers,\n\t\t\ton_titlebar, on_border, on_contents, on_workspace)) {\n\t\treturn;\n\t}\n\n\t// Handle clicking an empty workspace\n\tif (node && node->type == N_WORKSPACE) {\n\t\tif (state == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\t\tseat_set_focus(seat, node);\n\t\t\ttransaction_commit_dirty();\n\t\t}\n\t\tseat_pointer_notify_button(seat, time_msec, button, state);\n\t\treturn;\n\t}\n\n\t// Handle clicking a layer surface and its popups/subsurfaces\n\tstruct wlr_layer_surface_v1 *layer = NULL;\n\tif ((layer = toplevel_layer_surface_from_surface(surface))) {\n\t\tif (layer->current.keyboard_interactive) {\n\t\t\tseat_set_focus_layer(seat, layer);\n\t\t\ttransaction_commit_dirty();\n\t\t}\n\t\tif (state == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\t\tseatop_begin_down_on_surface(seat, surface, sx, sy);\n\t\t}\n\t\tseat_pointer_notify_button(seat, time_msec, button, state);\n\t\treturn;\n\t}\n\n\t// Handle tiling resize via border\n\tif (cont && resize_edge && button == BTN_LEFT &&\n\t\t\tstate == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) {\n\t\t// If a resize is triggered on a tabbed or stacked container, change\n\t\t// focus to the tab which already had inactive focus -- otherwise, we'd\n\t\t// change the active tab when the user probably just wanted to resize.\n\t\tstruct sway_container *cont_to_focus = cont;\n\t\tenum sway_container_layout layout = container_parent_layout(cont);\n\t\tif (layout == L_TABBED || layout == L_STACKED) {\n\t\t\tcont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node);\n\t\t}\n\n\t\tseat_set_focus_container(seat, cont_to_focus);\n\t\tseatop_begin_resize_tiling(seat, cont, edge);\n\t\treturn;\n\t}\n\n\t// Handle tiling resize via mod\n\tif (cont && !is_floating_or_child && mod_pressed && mod_resize_btn_pressed &&\n\t\t\tstate == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\tedge = 0;\n\t\tedge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ?\n\t\t\tWLR_EDGE_RIGHT : WLR_EDGE_LEFT;\n\t\tedge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ?\n\t\t\tWLR_EDGE_BOTTOM : WLR_EDGE_TOP;\n\n\t\tconst char *image = NULL;\n\t\tif (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) {\n\t\t\timage = \"nw-resize\";\n\t\t} else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) {\n\t\t\timage = \"ne-resize\";\n\t\t} else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) {\n\t\t\timage = \"se-resize\";\n\t\t} else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) {\n\t\t\timage = \"sw-resize\";\n\t\t}\n\t\tcursor_set_image(seat->cursor, image, NULL);\n\t\tseat_set_focus_container(seat, cont);\n\t\tseatop_begin_resize_tiling(seat, cont, edge);\n\t\treturn;\n\t}\n\n\t// Handle changing focus when clicking on a container\n\tif (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\t// Default case: focus the container that was just clicked.\n\t\tnode = &cont->node;\n\n\t\t// If the container is a tab/stacked container and the click happened\n\t\t// on a tab, switch to the tab. If the tab contents were already\n\t\t// focused, focus the tab container itself. If the tab container was\n\t\t// already focused, cycle back to focusing the tab contents.\n\t\tif (on_titlebar) {\n\t\t\tstruct sway_container *focus = seat_get_focused_container(seat);\n\t\t\tif (focus == cont || !container_has_ancestor(focus, cont)) {\n\t\t\t\tnode = seat_get_focus_inactive(seat, &cont->node);\n\t\t\t}\n\t\t}\n\n\t\tseat_set_focus(seat, node);\n\t\ttransaction_commit_dirty();\n\t}\n\n\t// Handle beginning floating move\n\tif (cont && is_floating_or_child && !is_fullscreen_or_child &&\n\t\t\tstate == WL_POINTER_BUTTON_STATE_PRESSED &&\n\t\t\t(mod_move_btn_pressed || titlebar_left_btn_pressed)) {\n\t\tseatop_begin_move_floating(seat, container_toplevel_ancestor(cont));\n\t\treturn;\n\t}\n\n\t// Handle beginning floating resize\n\tif (cont && is_floating_or_child && !is_fullscreen_or_child &&\n\t\t\tstate == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\t// Via border\n\t\tif (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {\n\t\t\tseat_set_focus_container(seat, cont);\n\t\t\tseatop_begin_resize_floating(seat, cont, resize_edge);\n\t\t\treturn;\n\t\t}\n\n\t\t// Via mod+click\n\t\tif (mod_resize_btn_pressed) {\n\t\t\tstruct sway_container *floater = container_toplevel_ancestor(cont);\n\t\t\tedge = 0;\n\t\t\tedge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ?\n\t\t\t\tWLR_EDGE_RIGHT : WLR_EDGE_LEFT;\n\t\t\tedge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?\n\t\t\t\tWLR_EDGE_BOTTOM : WLR_EDGE_TOP;\n\t\t\tseat_set_focus_container(seat, floater);\n\t\t\tseatop_begin_resize_floating(seat, floater, edge);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Handle moving a tiling container\n\tif (config->tiling_drag && (mod_move_btn_pressed || titlebar_left_btn_pressed) &&\n\t\t\tstate == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&\n\t\t\tcont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {\n\t\t// If moving a container by its title bar, use a threshold for the drag\n\t\tif (!mod_pressed && config->tiling_drag_threshold > 0) {\n\t\t\tseatop_begin_move_tiling_threshold(seat, cont);\n\t\t} else {\n\t\t\tseatop_begin_move_tiling(seat, cont);\n\t\t}\n\t\treturn;\n\t}\n\n\t// Handle mousedown on a container surface\n\tif (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\tseatop_begin_down(seat, cont, sx, sy);\n\t\tseat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED);\n\t\treturn;\n\t}\n\n\t// Handle clicking a container surface or decorations\n\tif (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\tseat_pointer_notify_button(seat, time_msec, button, state);\n\t\treturn;\n\t}\n\n#if WLR_HAS_XWAYLAND\n\t// Handle clicking on xwayland unmanaged view\n\tstruct wlr_xwayland_surface *xsurface;\n\tif (surface &&\n\t\t\t(xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&\n\t\t\txsurface->override_redirect &&\n\t\t\twlr_xwayland_surface_override_redirect_wants_focus(xsurface)) {\n\t\tstruct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;\n\t\twlr_xwayland_set_seat(xwayland, seat->wlr_seat);\n\t\tseat_set_focus_surface(seat, xsurface->surface, false);\n\t\ttransaction_commit_dirty();\n\t\tseat_pointer_notify_button(seat, time_msec, button, state);\n\t}\n#endif\n\n\tseat_pointer_notify_button(seat, time_msec, button, state);\n}\n\n/*------------------------------------------\\\n * Functions used by handle_pointer_motion  /\n *----------------------------------------*/\n\nstatic void check_focus_follows_mouse(struct sway_seat *seat,\n\t\tstruct seatop_default_event *e, struct sway_node *hovered_node) {\n\tstruct sway_node *focus = seat_get_focus(seat);\n\n\t// This is the case if a layer-shell surface is hovered.\n\t// If it's on another output, focus the active workspace there.\n\tif (!hovered_node) {\n\t\tstruct wlr_output *wlr_output = wlr_output_layout_output_at(\n\t\t\t\troot->output_layout, seat->cursor->cursor->x, seat->cursor->cursor->y);\n\t\tif (wlr_output == NULL) {\n\t\t\treturn;\n\t\t}\n\n\t\tstruct wlr_surface *surface = NULL;\n\t\tdouble sx, sy;\n\t\tnode_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y,\n\t\t\t\t&surface, &sx, &sy);\n\n\t\t// Focus topmost layer surface\n\t\tstruct wlr_layer_surface_v1 *layer = NULL;\n\t\tif ((layer = toplevel_layer_surface_from_surface(surface)) &&\n\t\t\t\tlayer->current.keyboard_interactive) {\n\t\t\tseat_set_focus_layer(seat, layer);\n\t\t\ttransaction_commit_dirty();\n\t\t\treturn;\n\t\t}\n\n\t\tstruct sway_output *hovered_output = wlr_output->data;\n\t\tif (focus && hovered_output != node_get_output(focus)) {\n\t\t\tstruct sway_workspace *ws = output_get_active_workspace(hovered_output);\n\t\t\tseat_set_focus(seat, &ws->node);\n\t\t\ttransaction_commit_dirty();\n\t\t}\n\t\treturn;\n\t}\n\n\t// If a workspace node is hovered (eg. in the gap area), only set focus if\n\t// the workspace is on a different output to the previous focus.\n\tif (focus && hovered_node->type == N_WORKSPACE) {\n\t\tstruct sway_output *focused_output = node_get_output(focus);\n\t\tstruct sway_output *hovered_output = node_get_output(hovered_node);\n\t\tif (hovered_output != focused_output) {\n\t\t\tseat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node));\n\t\t\ttransaction_commit_dirty();\n\t\t}\n\t\treturn;\n\t}\n\n\t// This is where we handle the common case. We don't want to focus inactive\n\t// tabs, hence the view_is_visible check.\n\tif (node_is_view(hovered_node) &&\n\t\t\tview_is_visible(hovered_node->sway_container->view)) {\n\t\t// e->previous_node is the node which the cursor was over previously.\n\t\t// If focus_follows_mouse is yes and the cursor got over the view due\n\t\t// to, say, a workspace switch, we don't want to set the focus.\n\t\t// But if focus_follows_mouse is \"always\", we do.\n\t\tif (hovered_node != e->previous_node ||\n\t\t\t\tconfig->focus_follows_mouse == FOLLOWS_ALWAYS) {\n\t\t\tseat_set_focus(seat, hovered_node);\n\t\t\ttransaction_commit_dirty();\n\t\t}\n\t}\n}\n\nstatic void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {\n\tstruct seatop_default_event *e = seat->seatop_data;\n\tstruct sway_cursor *cursor = seat->cursor;\n\n\tstruct wlr_surface *surface = NULL;\n\tdouble sx, sy;\n\tstruct sway_node *node = node_at_coords(seat,\n\t\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\tif (config->focus_follows_mouse != FOLLOWS_NO) {\n\t\tcheck_focus_follows_mouse(seat, e, node);\n\t}\n\n\tif (surface) {\n\t\tif (seat_is_input_allowed(seat, surface)) {\n\t\t\twlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy);\n\t\t\twlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);\n\t\t}\n\t} else {\n\t\tcursor_update_image(cursor, node);\n\t\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n\t}\n\n\tdrag_icons_update_position(seat);\n\n\te->previous_node = node;\n}\n\nstatic void handle_tablet_tool_motion(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec) {\n\tstruct seatop_default_event *e = seat->seatop_data;\n\tstruct sway_cursor *cursor = seat->cursor;\n\n\tstruct wlr_surface *surface = NULL;\n\tdouble sx, sy;\n\tstruct sway_node *node = node_at_coords(seat,\n\t\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\tif (config->focus_follows_mouse != FOLLOWS_NO) {\n\t\tcheck_focus_follows_mouse(seat, e, node);\n\t}\n\n\tif (surface) {\n\t\tif (seat_is_input_allowed(seat, surface)) {\n\t\t\twlr_tablet_v2_tablet_tool_notify_proximity_in(tool->tablet_v2_tool,\n\t\t\t\ttool->tablet->tablet_v2, surface);\n\t\t\twlr_tablet_v2_tablet_tool_notify_motion(tool->tablet_v2_tool, sx, sy);\n\t\t}\n\t} else {\n\t\tcursor_update_image(cursor, node);\n\t\twlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);\n\t}\n\n\tdrag_icons_update_position(seat);\n\n\te->previous_node = node;\n}\n\nstatic void handle_touch_down(struct sway_seat *seat,\n\t\tstruct wlr_touch_down_event *event, double lx, double ly) {\n\tstruct wlr_surface *surface = NULL;\n\tstruct wlr_seat *wlr_seat = seat->wlr_seat;\n\tstruct sway_cursor *cursor = seat->cursor;\n\tdouble sx, sy;\n\tnode_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy);\n\n\tif (surface && wlr_surface_accepts_touch(surface, wlr_seat)) {\n\t\tif (seat_is_input_allowed(seat, surface)) {\n\t\t\tcursor->simulating_pointer_from_touch = false;\n\t\t\tseatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly);\n\t\t}\n\t} else if (!cursor->simulating_pointer_from_touch &&\n\t\t\t(!surface || seat_is_input_allowed(seat, surface))) {\n\t\t// Fallback to cursor simulation.\n\t\t// The pointer_touch_id state is needed, so drags are not aborted when over\n\t\t// a surface supporting touch and multi touch events don't interfere.\n\t\tcursor->simulating_pointer_from_touch = true;\n\t\tcursor->pointer_touch_id = seat->touch_id;\n\t\tdouble dx, dy;\n\t\tdx = seat->touch_x - cursor->cursor->x;\n\t\tdy = seat->touch_y - cursor->cursor->y;\n\t\tpointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,\n\t\t\t\tdx, dy);\n\t\tdispatch_cursor_button(cursor, &event->touch->base, event->time_msec,\n\t\t\t\tBTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);\n\t}\n}\n\n/*----------------------------------------\\\n * Functions used by handle_pointer_axis  /\n *--------------------------------------*/\n\nstatic uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {\n\tswitch (event->orientation) {\n\tcase WL_POINTER_AXIS_VERTICAL_SCROLL:\n\t\treturn event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;\n\tcase WL_POINTER_AXIS_HORIZONTAL_SCROLL:\n\t\treturn event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;\n\tdefault:\n\t\tsway_log(SWAY_DEBUG, \"Unknown axis orientation\");\n\t\treturn 0;\n\t}\n}\n\nstatic void handle_pointer_axis(struct sway_seat *seat,\n\t\tstruct wlr_pointer_axis_event *event) {\n\tstruct sway_input_device *input_device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tstruct input_config *ic =\n\t\tinput_device ? input_device_get_config(input_device) : NULL;\n\tstruct sway_cursor *cursor = seat->cursor;\n\tstruct seatop_default_event *e = seat->seatop_data;\n\n\t// Determine what's under the cursor\n\tstruct wlr_surface *surface = NULL;\n\tdouble sx, sy;\n\tstruct sway_node *node = node_at_coords(seat,\n\t\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\tstruct sway_container *cont = node && node->type == N_CONTAINER ?\n\t\tnode->sway_container : NULL;\n\tenum wlr_edges edge = cont ? find_edge(cont, surface, cursor) : WLR_EDGE_NONE;\n\tbool on_border = edge != WLR_EDGE_NONE;\n\tbool on_titlebar = cont && !on_border && !surface;\n\tbool on_titlebar_border = cont && on_border &&\n\t\tcursor->cursor->y < cont->pending.content_y;\n\tbool on_contents = cont && !on_border && surface;\n\tbool on_workspace = node && node->type == N_WORKSPACE;\n\tfloat scroll_factor =\n\t\t(ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;\n\n\tbool handled = false;\n\n\t// Gather information needed for mouse bindings\n\tstruct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);\n\tuint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;\n\tstruct wlr_input_device *device =\n\t\tinput_device ? input_device->wlr_device : NULL;\n\tchar *dev_id = device ? input_device_get_identifier(device) : strdup(\"*\");\n\tuint32_t button = wl_axis_to_button(event);\n\n\t// Handle mouse bindings - x11 mouse buttons 4-7 - press event\n\tstruct sway_binding *binding = NULL;\n\tstate_add_button(e, button);\n\tbinding = get_active_mouse_binding(e, config->current_mode->mouse_bindings,\n\t\t\tmodifiers, false, on_titlebar, on_border, on_contents, on_workspace,\n\t\t\tdev_id);\n\tif (binding) {\n\t\tseat_execute_command(seat, binding);\n\t\thandled = true;\n\t}\n\n\t// Scrolling on a tabbed or stacked title bar (handled as press event)\n\tif (!handled && (on_titlebar || on_titlebar_border)) {\n\t\tstruct sway_node *new_focus;\n\t\tenum sway_container_layout layout = container_parent_layout(cont);\n\t\tif (layout == L_TABBED || layout == L_STACKED) {\n\t\t\tstruct sway_node *tabcontainer = node_get_parent(node);\n\t\t\tstruct sway_node *active =\n\t\t\t\tseat_get_active_tiling_child(seat, tabcontainer);\n\t\t\tlist_t *siblings = container_get_siblings(cont);\n\t\t\tint desired = list_find(siblings, active->sway_container) +\n\t\t\t\troundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);\n\t\t\tif (desired < 0) {\n\t\t\t\tdesired = 0;\n\t\t\t} else if (desired >= siblings->length) {\n\t\t\t\tdesired = siblings->length - 1;\n\t\t\t}\n\n\t\t\tstruct sway_container *new_sibling_con = siblings->items[desired];\n\t\t\tstruct sway_node *new_sibling = &new_sibling_con->node;\n\t\t\t// Use the focused child of the tabbed/stacked container, not the\n\t\t\t// container the user scrolled on.\n\t\t\tnew_focus = seat_get_focus_inactive(seat, new_sibling);\n\t\t} else {\n\t\t\tnew_focus = seat_get_focus_inactive(seat, &cont->node);\n\t\t}\n\n\t\tseat_set_focus(seat, new_focus);\n\t\ttransaction_commit_dirty();\n\t\thandled = true;\n\t}\n\n\t// Handle mouse bindings - x11 mouse buttons 4-7 - release event\n\tbinding = get_active_mouse_binding(e, config->current_mode->mouse_bindings,\n\t\t\tmodifiers, true, on_titlebar, on_border, on_contents, on_workspace,\n\t\t\tdev_id);\n\tstate_erase_button(e, button);\n\tif (binding) {\n\t\tseat_execute_command(seat, binding);\n\t\thandled = true;\n\t}\n\tfree(dev_id);\n\n\tif (!handled) {\n\t\twlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,\n\t\t\tevent->orientation, scroll_factor * event->delta,\n\t\t\troundf(scroll_factor * event->delta_discrete), event->source,\n\t\t\tevent->relative_direction);\n\t}\n}\n\n/*------------------------------------\\\n * Functions used by gesture support  /\n *----------------------------------*/\n\n/**\n * Check gesture binding for a specific gesture type and finger count.\n * Returns true if binding is present, false otherwise\n */\nstatic bool gesture_binding_check(list_t *bindings, enum gesture_type type,\n\t\tuint8_t fingers, struct sway_input_device *device) {\n\tchar *input =\n\t\tdevice ? input_device_get_identifier(device->wlr_device) : strdup(\"*\");\n\n\tfor (int i = 0; i < bindings->length; ++i) {\n\t\tstruct sway_gesture_binding *binding = bindings->items[i];\n\n\t\t// Check type and finger count\n\t\tif (!gesture_check(&binding->gesture, type, fingers)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check that input matches\n\t\tif (strcmp(binding->input, \"*\") != 0 &&\n\t\t\tstrcmp(binding->input, input) != 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfree(input);\n\n\t\treturn true;\n\t}\n\n\tfree(input);\n\n\treturn false;\n}\n\n/**\n * Return the gesture binding which matches gesture type, finger count\n * and direction, otherwise return null.\n */\nstatic struct sway_gesture_binding* gesture_binding_match(\n\t\tlist_t *bindings, struct gesture *gesture, const char *input) {\n\tstruct sway_gesture_binding *current = NULL;\n\n\t// Find best matching binding\n\tfor (int i = 0; i < bindings->length; ++i) {\n\t\tstruct sway_gesture_binding *binding = bindings->items[i];\n\t\tbool exact = binding->flags & BINDING_EXACT;\n\n\t\t// Check gesture matching\n\t\tif (!gesture_match(&binding->gesture, gesture, exact)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check input matching\n\t\tif (strcmp(binding->input, \"*\") != 0 &&\n\t\t\t\tstrcmp(binding->input, input) != 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If we already have a match ...\n\t\tif (current) {\n\t\t\t// ... check if input matching is equivalent\n\t\t\tif (strcmp(current->input, binding->input) == 0) {\n\n\t\t\t\t// ... - do not override an exact binding\n\t\t\t\tif (!exact && current->flags & BINDING_EXACT) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// ... - and ensure direction matching is better or equal\n\t\t\t\tif (gesture_compare(&current->gesture, &binding->gesture) > 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if (strcmp(binding->input, \"*\") == 0) {\n\t\t\t\t// ... do not accept worse input match\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Accept newer or better match\n\t\tcurrent = binding;\n\n\t\t// If exact binding and input is found, quit search\n\t\tif (strcmp(current->input, input) == 0 &&\n\t\t\t\tgesture_compare(&current->gesture, gesture) == 0) {\n\t\t\tbreak;\n\t\t}\n\t} // for all gesture bindings\n\n\treturn current;\n}\n\n// Wrapper around gesture_tracker_end to use tracker with sway bindings\nstatic struct sway_gesture_binding* gesture_tracker_end_and_match(\n\t\tstruct gesture_tracker *tracker, struct sway_input_device* device) {\n\t// Determine name of input that received gesture\n\tchar *input = device\n\t\t? input_device_get_identifier(device->wlr_device)\n\t\t: strdup(\"*\");\n\n\t// Match tracking result to binding\n\tstruct gesture *gesture = gesture_tracker_end(tracker);\n\tstruct sway_gesture_binding *binding = gesture_binding_match(\n\t\t\tconfig->current_mode->gesture_bindings, gesture, input);\n\tfree(gesture);\n\tfree(input);\n\n\treturn binding;\n}\n\n// Small wrapper around seat_execute_command to work on gesture bindings\nstatic void gesture_binding_execute(struct sway_seat *seat,\n\t\tstruct sway_gesture_binding *binding) {\n\tstruct sway_binding *dummy_binding =\n\t\tcalloc(1, sizeof(struct sway_binding));\n\tdummy_binding->type = BINDING_GESTURE;\n\tdummy_binding->command = binding->command;\n\n\tchar *description = gesture_to_string(&binding->gesture);\n\tsway_log(SWAY_DEBUG, \"executing gesture binding: %s\", description);\n\tfree(description);\n\n\tseat_execute_command(seat, dummy_binding);\n\n\tfree(dummy_binding);\n}\n\nstatic void handle_hold_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_hold_begin_event *event) {\n\t// Start tracking gesture if there is a matching binding ...\n\tstruct sway_input_device *device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tlist_t *bindings = config->current_mode->gesture_bindings;\n\tif (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {\n\t\tstruct seatop_default_event *seatop = seat->seatop_data;\n\t\tgesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);\n\t} else {\n\t\t// ... otherwise forward to client\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_hold_begin(\n\t\t\tserver.input->pointer_gestures, cursor->seat->wlr_seat,\n\t\t\tevent->time_msec, event->fingers);\n\t}\n}\n\nstatic void handle_hold_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_hold_end_event *event) {\n\t// Ensure that gesture is being tracked and was not cancelled\n\tstruct seatop_default_event *seatop = seat->seatop_data;\n\tif (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_hold_end(\n\t\t\tserver.input->pointer_gestures, cursor->seat->wlr_seat,\n\t\t\tevent->time_msec, event->cancelled);\n\t\treturn;\n\t}\n\tif (event->cancelled) {\n\t\tgesture_tracker_cancel(&seatop->gestures);\n\t\treturn;\n\t}\n\n\t// End gesture tracking and execute matched binding\n\tstruct sway_input_device *device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tstruct sway_gesture_binding *binding = gesture_tracker_end_and_match(\n\t\t&seatop->gestures, device);\n\n\tif (binding) {\n\t\tgesture_binding_execute(seat, binding);\n\t}\n}\n\nstatic void handle_pinch_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_begin_event *event) {\n\t// Start tracking gesture if there is a matching binding ...\n\tstruct sway_input_device *device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tlist_t *bindings = config->current_mode->gesture_bindings;\n\tif (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {\n\t\tstruct seatop_default_event *seatop = seat->seatop_data;\n\t\tgesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);\n\t} else {\n\t\t// ... otherwise forward to client\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_pinch_begin(\n\t\t\tserver.input->pointer_gestures, cursor->seat->wlr_seat,\n\t\t\tevent->time_msec, event->fingers);\n\t}\n}\n\nstatic void handle_pinch_update(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_update_event *event) {\n\t// Update any ongoing tracking ...\n\tstruct seatop_default_event *seatop = seat->seatop_data;\n\tif (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {\n\t\tgesture_tracker_update(&seatop->gestures, event->dx, event->dy,\n\t\t\tevent->scale, event->rotation);\n\t} else {\n\t\t// ... otherwise forward to client\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_pinch_update(\n\t\t\tserver.input->pointer_gestures,\n\t\t\tcursor->seat->wlr_seat,\n\t\t\tevent->time_msec, event->dx, event->dy,\n\t\t\tevent->scale, event->rotation);\n\t}\n}\n\nstatic void handle_pinch_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_pinch_end_event *event) {\n\t// Ensure that gesture is being tracked and was not cancelled\n\tstruct seatop_default_event *seatop = seat->seatop_data;\n\tif (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_pinch_end(\n\t\t\tserver.input->pointer_gestures, cursor->seat->wlr_seat,\n\t\t\tevent->time_msec, event->cancelled);\n\t\treturn;\n\t}\n\tif (event->cancelled) {\n\t\tgesture_tracker_cancel(&seatop->gestures);\n\t\treturn;\n\t}\n\n\t// End gesture tracking and execute matched binding\n\tstruct sway_input_device *device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tstruct sway_gesture_binding *binding = gesture_tracker_end_and_match(\n\t\t&seatop->gestures, device);\n\n\tif (binding) {\n\t\tgesture_binding_execute(seat, binding);\n\t}\n}\n\nstatic void handle_swipe_begin(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_begin_event *event) {\n\t// Start tracking gesture if there is a matching binding ...\n\tstruct sway_input_device *device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tlist_t *bindings = config->current_mode->gesture_bindings;\n\tif (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {\n\t\tstruct seatop_default_event *seatop = seat->seatop_data;\n\t\tgesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);\n\t} else {\n\t\t// ... otherwise forward to client\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_swipe_begin(\n\t\t\tserver.input->pointer_gestures, cursor->seat->wlr_seat,\n\t\t\tevent->time_msec, event->fingers);\n\t}\n}\n\nstatic void handle_swipe_update(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_update_event *event) {\n\n\t// Update any ongoing tracking ...\n\tstruct seatop_default_event *seatop = seat->seatop_data;\n\tif (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {\n\t\tgesture_tracker_update(&seatop->gestures,\n\t\t\tevent->dx, event->dy, NAN, NAN);\n\t} else {\n\t\t// ... otherwise forward to client\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_swipe_update(\n\t\t\tserver.input->pointer_gestures, cursor->seat->wlr_seat,\n\t\t\tevent->time_msec, event->dx, event->dy);\n\t}\n}\n\nstatic void handle_swipe_end(struct sway_seat *seat,\n\t\tstruct wlr_pointer_swipe_end_event *event) {\n\t// Ensure gesture is being tracked and was not cancelled\n\tstruct seatop_default_event *seatop = seat->seatop_data;\n\tif (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {\n\t\tstruct sway_cursor *cursor = seat->cursor;\n\t\twlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,\n\t\t\tcursor->seat->wlr_seat, event->time_msec, event->cancelled);\n\t\treturn;\n\t}\n\tif (event->cancelled) {\n\t\tgesture_tracker_cancel(&seatop->gestures);\n\t\treturn;\n\t}\n\n\t// End gesture tracking and execute matched binding\n\tstruct sway_input_device *device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tstruct sway_gesture_binding *binding = gesture_tracker_end_and_match(\n\t\t&seatop->gestures, device);\n\n\tif (binding) {\n\t\tgesture_binding_execute(seat, binding);\n\t}\n}\n\n/*----------------------------------\\\n * Functions used by handle_rebase  /\n *--------------------------------*/\n\nstatic void handle_rebase(struct sway_seat *seat, uint32_t time_msec) {\n\tstruct seatop_default_event *e = seat->seatop_data;\n\tstruct sway_cursor *cursor = seat->cursor;\n\tstruct wlr_surface *surface = NULL;\n\tdouble sx = 0.0, sy = 0.0;\n\te->previous_node = node_at_coords(seat,\n\t\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\tif (surface) {\n\t\tif (seat_is_input_allowed(seat, surface) && !cursor->hidden) {\n\t\t\twlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy);\n\t\t\twlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);\n\t\t}\n\t} else {\n\t\tcursor_update_image(cursor, e->previous_node);\n\t\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n\t}\n}\n\nstatic const struct sway_seatop_impl seatop_impl = {\n\t.button = handle_button,\n\t.pointer_motion = handle_pointer_motion,\n\t.pointer_axis = handle_pointer_axis,\n\t.tablet_tool_tip = handle_tablet_tool_tip,\n\t.tablet_tool_motion = handle_tablet_tool_motion,\n\t.hold_begin = handle_hold_begin,\n\t.hold_end = handle_hold_end,\n\t.pinch_begin = handle_pinch_begin,\n\t.pinch_update = handle_pinch_update,\n\t.pinch_end = handle_pinch_end,\n\t.swipe_begin = handle_swipe_begin,\n\t.swipe_update = handle_swipe_update,\n\t.swipe_end = handle_swipe_end,\n\t.touch_down = handle_touch_down,\n\t.rebase = handle_rebase,\n\t.allow_set_cursor = true,\n};\n\nvoid seatop_begin_default(struct sway_seat *seat) {\n\tseatop_end(seat);\n\n\tstruct seatop_default_event *e =\n\t\tcalloc(1, sizeof(struct seatop_default_event));\n\tsway_assert(e, \"Unable to allocate seatop_default_event\");\n\n\tseat->seatop_impl = &seatop_impl;\n\tseat->seatop_data = e;\n\n\tuint32_t time_msec = get_current_time_in_msec();\n\tseatop_rebase(seat, time_msec);\n}\n"
  },
  {
    "path": "sway/input/seatop_down.c",
    "content": "#include <float.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_tablet_v2.h>\n#include <wlr/types/wlr_touch.h>\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"log.h\"\n\nstruct seatop_touch_point_event {\n\tdouble ref_lx, ref_ly;         // touch's x/y at start of op\n\tdouble ref_con_lx, ref_con_ly; // container's x/y at start of op\n\tint32_t touch_id;\n\tstruct wl_list link;\n};\n\nstruct seatop_down_event {\n\tstruct sway_container *con;\n\tstruct sway_seat *seat;\n\tstruct wl_listener surface_destroy;\n\tstruct wlr_surface *surface;\n\tdouble ref_lx, ref_ly;         // cursor's x/y at start of op\n\tdouble ref_con_lx, ref_con_ly; // container's x/y at start of op\n\tstruct wl_list point_events;   // seatop_touch_point_event::link\n};\n\nstatic void handle_touch_motion(struct sway_seat *seat,\n\t\tstruct wlr_touch_motion_event *event, double lx, double ly) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\n\tstruct seatop_touch_point_event *point_event;\n\tbool found = false;\n\twl_list_for_each(point_event, &e->point_events, link) {\n\t\tif (point_event->touch_id == event->touch_id) {\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!found) {\n\t\treturn; // Probably not a point_event from this seatop_down\n\t}\n\n\tdouble moved_x = lx - point_event->ref_lx;\n\tdouble moved_y = ly - point_event->ref_ly;\n\tdouble sx = point_event->ref_con_lx + moved_x;\n\tdouble sy = point_event->ref_con_ly + moved_y;\n\n\twlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec,\n\t\tevent->touch_id, sx, sy);\n}\n\nstatic void handle_touch_up(struct sway_seat *seat,\n\t\tstruct wlr_touch_up_event *event) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\tstruct seatop_touch_point_event *point_event, *tmp;\n\n\twl_list_for_each_safe(point_event, tmp, &e->point_events, link) {\n\t\tif (point_event->touch_id == event->touch_id) {\n\t\t\twl_list_remove(&point_event->link);\n\t\t\tfree(point_event);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\twlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id);\n\n\tif (wl_list_empty(&e->point_events)) {\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic void handle_touch_down(struct sway_seat *seat,\n\t\tstruct wlr_touch_down_event *event, double lx, double ly) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\tdouble sx, sy;\n\tstruct wlr_surface *surface = NULL;\n\tstruct sway_node *focused_node = node_at_coords(seat, seat->touch_x,\n\t\tseat->touch_y, &surface, &sx, &sy);\n\n\tif (!surface || surface != e->surface) { // Must start from the initial surface\n\t\treturn;\n\t}\n\n\tstruct seatop_touch_point_event *point_event =\n\t\tcalloc(1, sizeof(struct seatop_touch_point_event));\n\tif (!sway_assert(point_event, \"Unable to allocate point_event\")) {\n\t\treturn;\n\t}\n\tpoint_event->touch_id = event->touch_id;\n\tpoint_event->ref_lx = lx;\n\tpoint_event->ref_ly = ly;\n\tpoint_event->ref_con_lx = sx;\n\tpoint_event->ref_con_ly = sy;\n\n\twl_list_insert(&e->point_events, &point_event->link);\n\n\twlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec,\n\t\t\tevent->touch_id, sx, sy);\n\n\tif (focused_node) {\n\t    seat_set_focus(seat, focused_node);\n\t\ttransaction_commit_dirty();\n\t}\n}\n\nstatic void handle_touch_cancel(struct sway_seat *seat,\n\t\tstruct wlr_touch_cancel_event *event) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\tstruct seatop_touch_point_event *point_event, *tmp;\n\n\twl_list_for_each_safe(point_event, tmp, &e->point_events, link) {\n\t\tif (point_event->touch_id == event->touch_id) {\n\t\t\twl_list_remove(&point_event->link);\n\t\t\tfree(point_event);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (e->surface) {\n\t\tstruct wl_client *client = wl_resource_get_client(e->surface->resource);\n\t\tstruct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client);\n\t\tif (seat_client != NULL) {\n\t\t\twlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client);\n\t\t}\n\t}\n\n\tif (wl_list_empty(&e->point_events)) {\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic void handle_pointer_axis(struct sway_seat *seat,\n\t\tstruct wlr_pointer_axis_event *event) {\n\tstruct sway_input_device *input_device =\n\t\tevent->pointer ? event->pointer->base.data : NULL;\n\tstruct input_config *ic =\n\t\tinput_device ? input_device_get_config(input_device) : NULL;\n\tfloat scroll_factor =\n\t\t(ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;\n\n\twlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec,\n\t\tevent->orientation, scroll_factor * event->delta,\n\t\troundf(scroll_factor * event->delta_discrete), event->source,\n\t\tevent->relative_direction);\n}\n\nstatic void handle_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tseat_pointer_notify_button(seat, time_msec, button, state);\n\n\tif (seat->cursor->pressed_button_count == 0) {\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\tif (seat_is_input_allowed(seat, e->surface)) {\n\t\tdouble moved_x = seat->cursor->cursor->x - e->ref_lx;\n\t\tdouble moved_y = seat->cursor->cursor->y - e->ref_ly;\n\t\tdouble sx = e->ref_con_lx + moved_x;\n\t\tdouble sy = e->ref_con_ly + moved_y;\n\t\twlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);\n\t}\n}\n\nstatic void handle_tablet_tool_tip(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec,\n\t\tenum wlr_tablet_tool_tip_state state) {\n\tif (state == WLR_TABLET_TOOL_TIP_UP) {\n\t\twlr_tablet_v2_tablet_tool_notify_up(tool->tablet_v2_tool);\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic void handle_tablet_tool_motion(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\tif (seat_is_input_allowed(seat, e->surface)) {\n\t\tdouble moved_x = seat->cursor->cursor->x - e->ref_lx;\n\t\tdouble moved_y = seat->cursor->cursor->y - e->ref_ly;\n\t\tdouble sx = e->ref_con_lx + moved_x;\n\t\tdouble sy = e->ref_con_ly + moved_y;\n\t\twlr_tablet_v2_tablet_tool_notify_motion(tool->tablet_v2_tool, sx, sy);\n\t}\n}\n\nstatic void handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct seatop_down_event *e =\n\t\twl_container_of(listener, e, surface_destroy);\n\tif (e) {\n\t\tseatop_begin_default(e->seat);\n\t}\n}\n\nstatic void handle_unref(struct sway_seat *seat, struct sway_container *con) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\tif (e->con == con) {\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic void handle_end(struct sway_seat *seat) {\n\tstruct seatop_down_event *e = seat->seatop_data;\n\twl_list_remove(&e->surface_destroy.link);\n}\n\nstatic const struct sway_seatop_impl seatop_impl = {\n\t.button = handle_button,\n\t.pointer_motion = handle_pointer_motion,\n\t.pointer_axis = handle_pointer_axis,\n\t.tablet_tool_tip = handle_tablet_tool_tip,\n\t.tablet_tool_motion = handle_tablet_tool_motion,\n\t.touch_motion = handle_touch_motion,\n\t.touch_up = handle_touch_up,\n\t.touch_down = handle_touch_down,\n\t.touch_cancel = handle_touch_cancel,\n\t.unref = handle_unref,\n\t.end = handle_end,\n\t.allow_set_cursor = true,\n};\n\nvoid seatop_begin_down(struct sway_seat *seat, struct sway_container *con,\n\t\tdouble sx, double sy) {\n\tseatop_begin_down_on_surface(seat, con->view->surface, sx, sy);\n\tstruct seatop_down_event *e = seat->seatop_data;\n\te->con = con;\n\n\tcontainer_raise_floating(con);\n\ttransaction_commit_dirty();\n}\n\nvoid seatop_begin_touch_down(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface, struct wlr_touch_down_event *event,\n\t\tdouble sx, double sy, double lx, double ly) {\n\tseatop_begin_down_on_surface(seat, surface, sx, sy);\n\thandle_touch_down(seat, event, lx, ly);\n}\n\nvoid seatop_begin_down_on_surface(struct sway_seat *seat,\n\t\tstruct wlr_surface *surface, double sx, double sy) {\n\tseatop_end(seat);\n\n\tstruct seatop_down_event *e =\n\t\tcalloc(1, sizeof(struct seatop_down_event));\n\tif (!sway_assert(e, \"Unable to allocate e\")) {\n\t\treturn;\n\t}\n\te->con = NULL;\n\te->seat = seat;\n\te->surface = surface;\n\twl_signal_add(&e->surface->events.destroy, &e->surface_destroy);\n\te->surface_destroy.notify = handle_destroy;\n\te->ref_lx = seat->cursor->cursor->x;\n\te->ref_ly = seat->cursor->cursor->y;\n\te->ref_con_lx = sx;\n\te->ref_con_ly = sy;\n\twl_list_init(&e->point_events);\n\n\tseat->seatop_impl = &seatop_impl;\n\tseat->seatop_data = e;\n}\n"
  },
  {
    "path": "sway/input/seatop_move_floating.c",
    "content": "#include <wlr/types/wlr_cursor.h>\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n\nstruct seatop_move_floating_event {\n\tstruct sway_container *con;\n\tdouble dx, dy; // cursor offset in container\n};\n\nstatic void finalize_move(struct sway_seat *seat) {\n\tstruct seatop_move_floating_event *e = seat->seatop_data;\n\n\t// We \"move\" the container to its own location\n\t// so it discovers its output again.\n\tcontainer_floating_move_to(e->con, e->con->pending.x, e->con->pending.y);\n\ttransaction_commit_dirty();\n\n\tseatop_begin_default(seat);\n}\n\nstatic void handle_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tif (seat->cursor->pressed_button_count == 0) {\n\t\tfinalize_move(seat);\n\t}\n}\n\nstatic void handle_tablet_tool_tip(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec,\n\t\tenum wlr_tablet_tool_tip_state state) {\n\tif (state == WLR_TABLET_TOOL_TIP_UP) {\n\t\tfinalize_move(seat);\n\t}\n}\nstatic void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {\n\tstruct seatop_move_floating_event *e = seat->seatop_data;\n\tstruct wlr_cursor *cursor = seat->cursor->cursor;\n\tcontainer_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_unref(struct sway_seat *seat, struct sway_container *con) {\n\tstruct seatop_move_floating_event *e = seat->seatop_data;\n\tif (e->con == con) {\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic const struct sway_seatop_impl seatop_impl = {\n\t.button = handle_button,\n\t.pointer_motion = handle_pointer_motion,\n\t.tablet_tool_tip = handle_tablet_tool_tip,\n\t.unref = handle_unref,\n};\n\nvoid seatop_begin_move_floating(struct sway_seat *seat,\n\t\tstruct sway_container *con) {\n\tseatop_end(seat);\n\n\tstruct sway_cursor *cursor = seat->cursor;\n\tstruct seatop_move_floating_event *e =\n\t\tcalloc(1, sizeof(struct seatop_move_floating_event));\n\tif (!e) {\n\t\treturn;\n\t}\n\te->con = con;\n\te->dx = cursor->cursor->x - con->pending.x;\n\te->dy = cursor->cursor->y - con->pending.y;\n\n\tseat->seatop_impl = &seatop_impl;\n\tseat->seatop_data = e;\n\n\tcontainer_raise_floating(con);\n\ttransaction_commit_dirty();\n\n\tcursor_set_image(cursor, \"grab\", NULL);\n\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n}\n"
  },
  {
    "path": "sway/input/seatop_move_tiling.c",
    "content": "#include <limits.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/util/edges.h>\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/node.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n\n// Thickness of the dropzone when dragging to the edge of a layout container\n#define DROP_LAYOUT_BORDER 30\n\n// Thickness of indicator when dropping onto a titlebar.  This should be a\n// multiple of 2.\n#define DROP_SPLIT_INDICATOR 10\n\nstruct seatop_move_tiling_event {\n\tstruct sway_container *con;\n\tstruct sway_node *target_node;\n\tenum wlr_edges target_edge;\n\tdouble ref_lx, ref_ly; // cursor's x/y at start of op\n\tbool threshold_reached;\n\tbool split_target;\n\tbool insert_after_target;\n\tstruct wlr_scene_rect *indicator_rect;\n};\n\nstatic void handle_end(struct sway_seat *seat) {\n\tstruct seatop_move_tiling_event *e = seat->seatop_data;\n\twlr_scene_node_destroy(&e->indicator_rect->node);\n\te->indicator_rect = NULL;\n}\n\nstatic void handle_motion_prethreshold(struct sway_seat *seat) {\n\tstruct seatop_move_tiling_event *e = seat->seatop_data;\n\tdouble cx = seat->cursor->cursor->x;\n\tdouble cy = seat->cursor->cursor->y;\n\tdouble sx = e->ref_lx;\n\tdouble sy = e->ref_ly;\n\n\t// Get the scaled threshold for the output. Even if the operation goes\n\t// across multiple outputs of varying scales, just use the scale for the\n\t// output that the cursor is currently on for simplicity.\n\tstruct wlr_output *wlr_output = wlr_output_layout_output_at(\n\t\t\troot->output_layout, cx, cy);\n\tdouble output_scale = wlr_output ? wlr_output->scale : 1;\n\tdouble threshold = config->tiling_drag_threshold * output_scale;\n\tthreshold *= threshold;\n\n\t// If the threshold has been exceeded, start the actual drag\n\tif ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {\n\t\twlr_scene_node_set_enabled(&e->indicator_rect->node, true);\n\t\te->threshold_reached = true;\n\t\tcursor_set_image(seat->cursor, \"grab\", NULL);\n\t}\n}\n\nstatic void resize_box(struct wlr_box *box, enum wlr_edges edge,\n\t\tint thickness) {\n\tswitch (edge) {\n\tcase WLR_EDGE_TOP:\n\t\tbox->height = thickness;\n\t\tbreak;\n\tcase WLR_EDGE_LEFT:\n\t\tbox->width = thickness;\n\t\tbreak;\n\tcase WLR_EDGE_RIGHT:\n\t\tbox->x = box->x + box->width - thickness;\n\t\tbox->width = thickness;\n\t\tbreak;\n\tcase WLR_EDGE_BOTTOM:\n\t\tbox->y = box->y + box->height - thickness;\n\t\tbox->height = thickness;\n\t\tbreak;\n\tcase WLR_EDGE_NONE:\n\t\tbox->x += thickness;\n\t\tbox->y += thickness;\n\t\tbox->width -= thickness * 2;\n\t\tbox->height -= thickness * 2;\n\t\tbreak;\n\t}\n}\n\nstatic void split_border(double pos, int offset, int len, int n_children,\n\t\tint avoid, int *out_pos, bool *out_after) {\n\tint region = 2 * n_children * (pos - offset) / len;\n\t// If the cursor is over the right side of a left-adjacent titlebar, or the\n\t// left side of a right-adjacent titlebar, it's position when dropped will\n\t// be the same.  To avoid this, shift the region for adjacent containers.\n\tif (avoid >= 0) {\n\t\tif (region == 2 * avoid - 1 || region == 2 * avoid) {\n\t\t\tregion--;\n\t\t} else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) {\n\t\t\tregion++;\n\t\t}\n\t}\n\n\tint child_index = (region + 1) / 2;\n\t*out_after = region % 2;\n\t// When dropping at the beginning or end of a container, show the drop\n\t// region within the container boundary, otherwise show it on top of the\n\t// border between two titlebars.\n\tif (child_index == 0) {\n\t\t*out_pos = offset;\n\t} else if (child_index == n_children) {\n\t\t*out_pos = offset + len - DROP_SPLIT_INDICATOR;\n\t} else {\n\t\t*out_pos = offset + child_index * len / n_children -\n\t\t\tDROP_SPLIT_INDICATOR / 2;\n\t}\n}\n\nstatic bool split_titlebar(struct sway_node *node, struct sway_container *avoid,\n\t\tstruct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) {\n\tstruct sway_container *con = node->sway_container;\n\tstruct sway_node *parent = &con->pending.parent->node;\n\tint title_height = container_titlebar_height();\n\tstruct wlr_box box;\n\tint n_children, avoid_index;\n\tenum sway_container_layout layout =\n\t\tparent ? node_get_layout(parent) : L_NONE;\n\tif (layout == L_TABBED || layout == L_STACKED) {\n\t\tnode_get_box(parent, &box);\n\t\tn_children = node_get_children(parent)->length;\n\t\tavoid_index = list_find(node_get_children(parent), avoid);\n\t} else {\n\t\tnode_get_box(node, &box);\n\t\tn_children = 1;\n\t\tavoid_index = -1;\n\t}\n\tif (layout == L_STACKED && cursor->y < box.y + title_height * n_children) {\n\t\t// Drop into stacked titlebars.\n\t\ttitle_box->width = box.width;\n\t\ttitle_box->height = DROP_SPLIT_INDICATOR;\n\t\ttitle_box->x = box.x;\n\t\tsplit_border(cursor->y, box.y, title_height * n_children,\n\t\t\tn_children, avoid_index, &title_box->y, after);\n\t\treturn true;\n\t} else if (layout != L_STACKED && cursor->y < box.y + title_height) {\n\t\t// Drop into side-by-side titlebars.\n\t\ttitle_box->width = DROP_SPLIT_INDICATOR;\n\t\ttitle_box->height = title_height;\n\t\ttitle_box->y = box.y;\n\t\tsplit_border(cursor->x, box.x, box.width, n_children,\n\t\t\tavoid_index, &title_box->x, after);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) {\n\twlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y);\n\twlr_scene_rect_set_size(e->indicator_rect, box->width, box->height);\n}\n\nstatic void handle_motion_postthreshold(struct sway_seat *seat) {\n\tstruct seatop_move_tiling_event *e = seat->seatop_data;\n\te->split_target = false;\n\tstruct wlr_surface *surface = NULL;\n\tdouble sx, sy;\n\tstruct sway_cursor *cursor = seat->cursor;\n\tstruct sway_node *node = node_at_coords(seat,\n\t\t\tcursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);\n\n\tif (!node) {\n\t\t// Eg. hovered over a layer surface such as swaybar\n\t\te->target_node = NULL;\n\t\te->target_edge = WLR_EDGE_NONE;\n\t\treturn;\n\t}\n\n\tif (node->type == N_WORKSPACE) {\n\t\t// Empty workspace\n\t\te->target_node = node;\n\t\te->target_edge = WLR_EDGE_NONE;\n\n\t\tstruct wlr_box drop_box;\n\t\tworkspace_get_box(node->sway_workspace, &drop_box);\n\t\tupdate_indicator(e, &drop_box);\n\t\treturn;\n\t}\n\n\t// Deny moving within own workspace if this is the only child\n\tstruct sway_container *con = node->sway_container;\n\tif (workspace_num_tiling_views(e->con->pending.workspace) == 1 &&\n\t\t\tcon->pending.workspace == e->con->pending.workspace) {\n\t\te->target_node = NULL;\n\t\te->target_edge = WLR_EDGE_NONE;\n\t\treturn;\n\t}\n\n\tstruct wlr_box drop_box = {\n\t\t.x = con->pending.content_x,\n\t\t.y = con->pending.content_y,\n\t\t.width = con->pending.content_width,\n\t\t.height = con->pending.content_height,\n\t};\n\n\t// Check if the cursor is over a tilebar only if the destination\n\t// container is not a descendant of the source container.\n\tif (!surface && !container_has_ancestor(con, e->con) &&\n\t\t\tsplit_titlebar(node, e->con, cursor->cursor,\n\t\t\t\t&drop_box, &e->insert_after_target)) {\n\t\t// Don't allow dropping over the source container's titlebar\n\t\t// to give users a chance to cancel a drag operation.\n\t\tif (con == e->con) {\n\t\t\te->target_node = NULL;\n\t\t} else {\n\t\t\te->target_node = node;\n\t\t\te->split_target = true;\n\t\t}\n\t\te->target_edge = WLR_EDGE_NONE;\n\t\tupdate_indicator(e, &drop_box);\n\t\treturn;\n\t}\n\n\t// Traverse the ancestors, trying to find a layout container perpendicular\n\t// to the edge. Eg. close to the top or bottom of a horiz layout.\n\tint thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER;\n\tint thresh_bottom = con->pending.content_y +\n\t\tcon->pending.content_height - DROP_LAYOUT_BORDER;\n\tint thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER;\n\tint thresh_right = con->pending.content_x +\n\t\tcon->pending.content_width - DROP_LAYOUT_BORDER;\n\twhile (con) {\n\t\tenum wlr_edges edge = WLR_EDGE_NONE;\n\t\tenum sway_container_layout layout = container_parent_layout(con);\n\t\tstruct wlr_box box;\n\t\tnode_get_box(node_get_parent(&con->node), &box);\n\t\tif (layout == L_HORIZ || layout == L_TABBED) {\n\t\t\tif (cursor->cursor->y < thresh_top) {\n\t\t\t\tedge = WLR_EDGE_TOP;\n\t\t\t\tbox.height = thresh_top - box.y;\n\t\t\t} else if (cursor->cursor->y > thresh_bottom) {\n\t\t\t\tedge = WLR_EDGE_BOTTOM;\n\t\t\t\tbox.height = box.y + box.height - thresh_bottom;\n\t\t\t\tbox.y = thresh_bottom;\n\t\t\t}\n\t\t} else if (layout == L_VERT || layout == L_STACKED) {\n\t\t\tif (cursor->cursor->x < thresh_left) {\n\t\t\t\tedge = WLR_EDGE_LEFT;\n\t\t\t\tbox.width = thresh_left - box.x;\n\t\t\t} else if (cursor->cursor->x > thresh_right) {\n\t\t\t\tedge = WLR_EDGE_RIGHT;\n\t\t\t\tbox.width = box.x + box.width - thresh_right;\n\t\t\t\tbox.x = thresh_right;\n\t\t\t}\n\t\t}\n\t\tif (edge) {\n\t\t\te->target_node = node_get_parent(&con->node);\n\t\t\tif (e->target_node == &e->con->node) {\n\t\t\t\te->target_node = node_get_parent(e->target_node);\n\t\t\t}\n\t\t\te->target_edge = edge;\n\t\t\tupdate_indicator(e, &box);\n\t\t\treturn;\n\t\t}\n\t\tcon = con->pending.parent;\n\t}\n\n\t// Use the hovered view - but we must be over the actual surface\n\tcon = node->sway_container;\n\tif (!con->view || !con->view->surface || node == &e->con->node\n\t\t\t|| node_has_ancestor(node, &e->con->node)) {\n\t\te->target_node = NULL;\n\t\te->target_edge = WLR_EDGE_NONE;\n\t\treturn;\n\t}\n\n\t// Find the closest edge\n\tsize_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3;\n\tsize_t closest_dist = INT_MAX;\n\tsize_t dist;\n\te->target_edge = WLR_EDGE_NONE;\n\tif ((dist = cursor->cursor->y - con->pending.y) < closest_dist) {\n\t\tclosest_dist = dist;\n\t\te->target_edge = WLR_EDGE_TOP;\n\t}\n\tif ((dist = cursor->cursor->x - con->pending.x) < closest_dist) {\n\t\tclosest_dist = dist;\n\t\te->target_edge = WLR_EDGE_LEFT;\n\t}\n\tif ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) {\n\t\tclosest_dist = dist;\n\t\te->target_edge = WLR_EDGE_RIGHT;\n\t}\n\tif ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) {\n\t\tclosest_dist = dist;\n\t\te->target_edge = WLR_EDGE_BOTTOM;\n\t}\n\n\tif (closest_dist > thickness) {\n\t\te->target_edge = WLR_EDGE_NONE;\n\t}\n\n\te->target_node = node;\n\tresize_box(&drop_box, e->target_edge, thickness);\n\tupdate_indicator(e, &drop_box);\n}\n\nstatic void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {\n\tstruct seatop_move_tiling_event *e = seat->seatop_data;\n\tif (e->threshold_reached) {\n\t\thandle_motion_postthreshold(seat);\n\t} else {\n\t\thandle_motion_prethreshold(seat);\n\t}\n\ttransaction_commit_dirty();\n}\n\nstatic bool is_parallel(enum sway_container_layout layout,\n\t\tenum wlr_edges edge) {\n\tbool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;\n\tbool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;\n\treturn layout_is_horiz == edge_is_horiz;\n}\n\nstatic void finalize_move(struct sway_seat *seat) {\n\tstruct seatop_move_tiling_event *e = seat->seatop_data;\n\n\tif (!e->target_node) {\n\t\tseatop_begin_default(seat);\n\t\treturn;\n\t}\n\n\tstruct sway_container *con = e->con;\n\tstruct sway_container *old_parent = con->pending.parent;\n\tstruct sway_workspace *old_ws = con->pending.workspace;\n\tstruct sway_node *target_node = e->target_node;\n\tstruct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?\n\t\ttarget_node->sway_workspace : target_node->sway_container->pending.workspace;\n\tenum wlr_edges edge = e->target_edge;\n\tint after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;\n\tbool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER &&\n\t\t!e->split_target;\n\n\tif (!swap) {\n\t\tcontainer_detach(con);\n\t}\n\n\t// Moving container into empty workspace\n\tif (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {\n\t\tcon = workspace_add_tiling(new_ws, con);\n\t} else if (e->split_target) {\n\t\tstruct sway_container *target = target_node->sway_container;\n\t\tenum sway_container_layout layout = container_parent_layout(target);\n\t\tif (layout != L_TABBED && layout != L_STACKED) {\n\t\t\tcontainer_split(target, L_TABBED);\n\t\t}\n\t\tcontainer_add_sibling(target, con, e->insert_after_target);\n\t\tipc_event_window(con, \"move\");\n\t} else if (target_node->type == N_CONTAINER) {\n\t\t// Moving container before/after another\n\t\tstruct sway_container *target = target_node->sway_container;\n\t\tif (swap) {\n\t\t\tcontainer_swap(target_node->sway_container, con);\n\t\t} else {\n\t\t\tenum sway_container_layout layout = container_parent_layout(target);\n\t\t\tif (edge && !is_parallel(layout, edge)) {\n\t\t\t\tenum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||\n\t\t\t\t\tedge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;\n\t\t\t\tcontainer_split(target, new_layout);\n\t\t\t}\n\t\t\tcontainer_add_sibling(target, con, after);\n\t\t\tipc_event_window(con, \"move\");\n\t\t}\n\t} else {\n\t\t// Target is a workspace which requires splitting\n\t\tenum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||\n\t\t\tedge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;\n\t\tworkspace_split(new_ws, new_layout);\n\t\tworkspace_insert_tiling(new_ws, con, after);\n\t}\n\n\tif (old_parent) {\n\t\tcontainer_reap_empty(old_parent);\n\t}\n\n\t// This is a bit dirty, but we'll set the dimensions to that of a sibling.\n\t// I don't think there's any other way to make it consistent without\n\t// changing how we auto-size containers.\n\tlist_t *siblings = container_get_siblings(con);\n\tif (siblings->length > 1) {\n\t\tint index = list_find(siblings, con);\n\t\tstruct sway_container *sibling = index == 0 ?\n\t\t\tsiblings->items[1] : siblings->items[index - 1];\n\t\tcon->pending.width = sibling->pending.width;\n\t\tcon->pending.height = sibling->pending.height;\n\t\tcon->width_fraction = sibling->width_fraction;\n\t\tcon->height_fraction = sibling->height_fraction;\n\t}\n\n\tarrange_workspace(old_ws);\n\tif (new_ws != old_ws) {\n\t\tarrange_workspace(new_ws);\n\t}\n\n\ttransaction_commit_dirty();\n\tseatop_begin_default(seat);\n}\n\nstatic void handle_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tif (seat->cursor->pressed_button_count == 0) {\n\t\tfinalize_move(seat);\n\t}\n}\n\nstatic void handle_tablet_tool_tip(struct sway_seat *seat,\n\t\tstruct sway_tablet_tool *tool, uint32_t time_msec,\n\t\tenum wlr_tablet_tool_tip_state state) {\n\tif (state == WLR_TABLET_TOOL_TIP_UP) {\n\t\tfinalize_move(seat);\n\t}\n}\n\nstatic void handle_unref(struct sway_seat *seat, struct sway_container *con) {\n\tstruct seatop_move_tiling_event *e = seat->seatop_data;\n\tif (e->target_node == &con->node) { // Drop target\n\t\te->target_node = NULL;\n\t}\n\tif (e->con == con) { // The container being moved\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic const struct sway_seatop_impl seatop_impl = {\n\t.button = handle_button,\n\t.pointer_motion = handle_pointer_motion,\n\t.tablet_tool_tip = handle_tablet_tool_tip,\n\t.unref = handle_unref,\n\t.end = handle_end,\n};\n\nvoid seatop_begin_move_tiling_threshold(struct sway_seat *seat,\n\t\tstruct sway_container *con) {\n\tseatop_end(seat);\n\n\tstruct seatop_move_tiling_event *e =\n\t\tcalloc(1, sizeof(struct seatop_move_tiling_event));\n\tif (!e) {\n\t\treturn;\n\t}\n\n\tconst float *indicator = config->border_colors.focused.indicator;\n\tfloat color[4] = {\n\t\tindicator[0] * .5,\n\t\tindicator[1] * .5,\n\t\tindicator[2] * .5,\n\t\tindicator[3] * .5,\n\t};\n\te->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color);\n\tif (!e->indicator_rect) {\n\t\tfree(e);\n\t\treturn;\n\t}\n\n\te->con = con;\n\te->ref_lx = seat->cursor->cursor->x;\n\te->ref_ly = seat->cursor->cursor->y;\n\n\tseat->seatop_impl = &seatop_impl;\n\tseat->seatop_data = e;\n\n\tcontainer_raise_floating(con);\n\ttransaction_commit_dirty();\n\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n}\n\nvoid seatop_begin_move_tiling(struct sway_seat *seat,\n\t\tstruct sway_container *con) {\n\tseatop_begin_move_tiling_threshold(seat, con);\n\tstruct seatop_move_tiling_event *e = seat->seatop_data;\n\tif (e) {\n\t\te->threshold_reached = true;\n\t\tcursor_set_image(seat->cursor, \"grab\", NULL);\n\t}\n}\n"
  },
  {
    "path": "sway/input/seatop_resize_floating.c",
    "content": "#include <limits.h>\n#include <wlr/types/wlr_cursor.h>\n#include <wlr/types/wlr_xcursor_manager.h>\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/tree/container.h\"\n\nstruct seatop_resize_floating_event {\n\tstruct sway_container *con;\n\tenum wlr_edges edge;\n\tbool preserve_ratio;\n\tdouble ref_lx, ref_ly;         // cursor's x/y at start of op\n\tdouble ref_width, ref_height;  // container's size at start of op\n\tdouble ref_con_lx, ref_con_ly; // container's x/y at start of op\n};\n\nstatic void handle_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tstruct seatop_resize_floating_event *e = seat->seatop_data;\n\tstruct sway_container *con = e->con;\n\n\tif (seat->cursor->pressed_button_count == 0) {\n\t\tcontainer_set_resizing(con, false);\n\t\tarrange_container(con); // Send configure w/o resizing hint\n\t\ttransaction_commit_dirty();\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {\n\tstruct seatop_resize_floating_event *e = seat->seatop_data;\n\tstruct sway_container *con = e->con;\n\tenum wlr_edges edge = e->edge;\n\tstruct sway_cursor *cursor = seat->cursor;\n\n\t// The amount the mouse has moved since the start of the resize operation\n\t// Positive is down/right\n\tdouble mouse_move_x = cursor->cursor->x - e->ref_lx;\n\tdouble mouse_move_y = cursor->cursor->y - e->ref_ly;\n\n\tif (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {\n\t\tmouse_move_x = 0;\n\t}\n\tif (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {\n\t\tmouse_move_y = 0;\n\t}\n\n\tdouble grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;\n\tdouble grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;\n\n\tif (e->preserve_ratio) {\n\t\tdouble x_multiplier = grow_width / e->ref_width;\n\t\tdouble y_multiplier = grow_height / e->ref_height;\n\t\tdouble max_multiplier = fmax(x_multiplier, y_multiplier);\n\t\tgrow_width = e->ref_width * max_multiplier;\n\t\tgrow_height = e->ref_height * max_multiplier;\n\t}\n\n\tstruct sway_container_state *state = &con->current;\n\tdouble border_width = 0.0;\n\tif (con->current.border == B_NORMAL || con->current.border == B_PIXEL) {\n\t\tborder_width = state->border_thickness * 2;\n\t}\n\tdouble border_height = 0.0;\n\tif (con->current.border == B_NORMAL) {\n\t\tborder_height += container_titlebar_height();\n\t\tborder_height += state->border_thickness;\n\t} else if (con->current.border == B_PIXEL) {\n\t\tborder_height += state->border_thickness * 2;\n\t}\n\n\t// Determine new width/height, and accommodate for floating min/max values\n\tdouble width = e->ref_width + grow_width;\n\tdouble height = e->ref_height + grow_height;\n\tint min_width, max_width, min_height, max_height;\n\tfloating_calculate_constraints(&min_width, &max_width,\n\t\t&min_height, &max_height);\n\twidth = fmin(width, max_width - border_width);\n\twidth = fmax(width, min_width + border_width);\n\twidth = fmax(width, 1);\n\theight = fmin(height, max_height - border_height);\n\theight = fmax(height, min_height + border_height);\n\theight = fmax(height, 1);\n\n\t// Apply the view's min/max size\n\tif (con->view) {\n\t\tdouble view_min_width, view_max_width, view_min_height, view_max_height;\n\t\tview_get_constraints(con->view, &view_min_width, &view_max_width,\n\t\t\t\t&view_min_height, &view_max_height);\n\t\twidth = fmin(width, view_max_width - border_width);\n\t\twidth = fmax(width, view_min_width + border_width);\n\t\twidth = fmax(width, 1);\n\t\theight = fmin(height, view_max_height - border_height);\n\t\theight = fmax(height, view_min_height + border_height);\n\t\theight = fmax(height, 1);\n\n\t}\n\n\t// Recalculate these, in case we hit a min/max limit\n\tgrow_width = width - e->ref_width;\n\tgrow_height = height - e->ref_height;\n\n\t// Determine grow x/y values - these are relative to the container's x/y at\n\t// the start of the resize operation.\n\tdouble grow_x = 0, grow_y = 0;\n\tif (edge & WLR_EDGE_LEFT) {\n\t\tgrow_x = -grow_width;\n\t} else if (edge & WLR_EDGE_RIGHT) {\n\t\tgrow_x = 0;\n\t} else {\n\t\tgrow_x = -grow_width / 2;\n\t}\n\tif (edge & WLR_EDGE_TOP) {\n\t\tgrow_y = -grow_height;\n\t} else if (edge & WLR_EDGE_BOTTOM) {\n\t\tgrow_y = 0;\n\t} else {\n\t\tgrow_y = -grow_height / 2;\n\t}\n\n\t// Determine the amounts we need to bump everything relative to the current\n\t// size.\n\tint relative_grow_width = width - con->pending.width;\n\tint relative_grow_height = height - con->pending.height;\n\tint relative_grow_x = (e->ref_con_lx + grow_x) - con->pending.x;\n\tint relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y;\n\n\t// Actually resize stuff\n\tcon->pending.x += relative_grow_x;\n\tcon->pending.y += relative_grow_y;\n\tcon->pending.width += relative_grow_width;\n\tcon->pending.height += relative_grow_height;\n\n\tcon->pending.content_x += relative_grow_x;\n\tcon->pending.content_y += relative_grow_y;\n\tcon->pending.content_width += relative_grow_width;\n\tcon->pending.content_height += relative_grow_height;\n\n\tarrange_container(con);\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_unref(struct sway_seat *seat, struct sway_container *con) {\n\tstruct seatop_resize_floating_event *e = seat->seatop_data;\n\tif (e->con == con) {\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic const struct sway_seatop_impl seatop_impl = {\n\t.button = handle_button,\n\t.pointer_motion = handle_pointer_motion,\n\t.unref = handle_unref,\n};\n\nvoid seatop_begin_resize_floating(struct sway_seat *seat,\n\t\tstruct sway_container *con, enum wlr_edges edge) {\n\tseatop_end(seat);\n\n\tstruct seatop_resize_floating_event *e =\n\t\tcalloc(1, sizeof(struct seatop_resize_floating_event));\n\tif (!e) {\n\t\treturn;\n\t}\n\te->con = con;\n\n\tstruct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);\n\te->preserve_ratio = keyboard &&\n\t\t(wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);\n\n\te->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;\n\te->ref_lx = seat->cursor->cursor->x;\n\te->ref_ly = seat->cursor->cursor->y;\n\te->ref_con_lx = con->pending.x;\n\te->ref_con_ly = con->pending.y;\n\te->ref_width = con->pending.width;\n\te->ref_height = con->pending.height;\n\n\tseat->seatop_impl = &seatop_impl;\n\tseat->seatop_data = e;\n\n\tcontainer_set_resizing(con, true);\n\tcontainer_raise_floating(con);\n\ttransaction_commit_dirty();\n\n\tconst char *image = edge == WLR_EDGE_NONE ?\n\t\t\"se-resize\" : wlr_xcursor_get_resize_name(edge);\n\tcursor_set_image(seat->cursor, image, NULL);\n\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n}\n"
  },
  {
    "path": "sway/input/seatop_resize_tiling.c",
    "content": "#include <wlr/types/wlr_cursor.h>\n#include <wlr/util/edges.h>\n#include \"sway/commands.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n\nstruct seatop_resize_tiling_event {\n\tstruct sway_container *con;    // leaf container\n\n\t// con, or ancestor of con which will be resized horizontally/vertically\n\tstruct sway_container *h_con;\n\tstruct sway_container *v_con;\n\n\t// sibling con(s) that will be resized to accommodate\n\tstruct sway_container *h_sib;\n\tstruct sway_container *v_sib;\n\n\tenum wlr_edges edge;\n\tenum wlr_edges edge_x, edge_y;\n\tdouble ref_lx, ref_ly;         // cursor's x/y at start of op\n\tdouble h_con_orig_width;       // width of the horizontal ancestor at start\n\tdouble v_con_orig_height;      // height of the vertical ancestor at start\n};\n\nstatic struct sway_container *container_get_resize_sibling(\n\t\tstruct sway_container *con, uint32_t edge) {\n\tif (!con) {\n\t\treturn NULL;\n\t}\n\n\tlist_t *siblings = container_get_siblings(con);\n\tint index = container_sibling_index(con);\n\tint offset = edge & (WLR_EDGE_TOP | WLR_EDGE_LEFT) ? -1 : 1;\n\n\tif (siblings->length == 1) {\n\t\treturn NULL;\n\t} else {\n\t\treturn siblings->items[index + offset];\n\t}\n}\n\nstatic void handle_button(struct sway_seat *seat, uint32_t time_msec,\n\t\tstruct wlr_input_device *device, uint32_t button,\n\t\tenum wl_pointer_button_state state) {\n\tstruct seatop_resize_tiling_event *e = seat->seatop_data;\n\n\tif (seat->cursor->pressed_button_count == 0) {\n\t\tif (e->h_con) {\n\t\t\tcontainer_set_resizing(e->h_con, false);\n\t\t\tcontainer_set_resizing(e->h_sib, false);\n\t\t\tif (e->h_con->pending.parent) {\n\t\t\t\tarrange_container(e->h_con->pending.parent);\n\t\t\t} else {\n\t\t\t\tarrange_workspace(e->h_con->pending.workspace);\n\t\t\t}\n\t\t}\n\t\tif (e->v_con) {\n\t\t\tcontainer_set_resizing(e->v_con, false);\n\t\t\tcontainer_set_resizing(e->v_sib, false);\n\t\t\tif (e->v_con->pending.parent) {\n\t\t\t\tarrange_container(e->v_con->pending.parent);\n\t\t\t} else {\n\t\t\t\tarrange_workspace(e->v_con->pending.workspace);\n\t\t\t}\n\t\t}\n\t\ttransaction_commit_dirty();\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {\n\tstruct seatop_resize_tiling_event *e = seat->seatop_data;\n\tint amount_x = 0;\n\tint amount_y = 0;\n\tint moved_x = seat->cursor->cursor->x - e->ref_lx;\n\tint moved_y = seat->cursor->cursor->y - e->ref_ly;\n\n\tif (e->h_con) {\n\t\tif (e->edge & WLR_EDGE_LEFT) {\n\t\t\tamount_x = (e->h_con_orig_width - moved_x) - e->h_con->pending.width;\n\t\t} else if (e->edge & WLR_EDGE_RIGHT) {\n\t\t\tamount_x = (e->h_con_orig_width + moved_x) - e->h_con->pending.width;\n\t\t}\n\t}\n\tif (e->v_con) {\n\t\tif (e->edge & WLR_EDGE_TOP) {\n\t\t\tamount_y = (e->v_con_orig_height - moved_y) - e->v_con->pending.height;\n\t\t} else if (e->edge & WLR_EDGE_BOTTOM) {\n\t\t\tamount_y = (e->v_con_orig_height + moved_y) - e->v_con->pending.height;\n\t\t}\n\t}\n\n\tif (amount_x != 0) {\n\t\tcontainer_resize_tiled(e->h_con, e->edge_x, amount_x);\n\t}\n\tif (amount_y != 0) {\n\t\tcontainer_resize_tiled(e->v_con, e->edge_y, amount_y);\n\t}\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_unref(struct sway_seat *seat, struct sway_container *con) {\n\tstruct seatop_resize_tiling_event *e = seat->seatop_data;\n\tif (e->con == con || e->h_sib == con || e->v_sib == con) {\n\t\tseatop_begin_default(seat);\n\t}\n}\n\nstatic const struct sway_seatop_impl seatop_impl = {\n\t.button = handle_button,\n\t.pointer_motion = handle_pointer_motion,\n\t.unref = handle_unref,\n};\n\nvoid seatop_begin_resize_tiling(struct sway_seat *seat,\n\t\tstruct sway_container *con, enum wlr_edges edge) {\n\tseatop_end(seat);\n\n\tstruct seatop_resize_tiling_event *e =\n\t\tcalloc(1, sizeof(struct seatop_resize_tiling_event));\n\tif (!e) {\n\t\treturn;\n\t}\n\te->con = con;\n\te->edge = edge;\n\n\te->ref_lx = seat->cursor->cursor->x;\n\te->ref_ly = seat->cursor->cursor->y;\n\n\tif (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) {\n\t\te->edge_x = edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT);\n\t\te->h_con = container_find_resize_parent(e->con, e->edge_x);\n\t\te->h_sib = container_get_resize_sibling(e->h_con, e->edge_x);\n\n\t\tif (e->h_con) {\n\t\t\tcontainer_set_resizing(e->h_con, true);\n\t\t\tcontainer_set_resizing(e->h_sib, true);\n\t\t\te->h_con_orig_width = e->h_con->pending.width;\n\t\t}\n\t}\n\tif (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) {\n\t\te->edge_y = edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM);\n\t\te->v_con = container_find_resize_parent(e->con, e->edge_y);\n\t\te->v_sib = container_get_resize_sibling(e->v_con, e->edge_y);\n\n\t\tif (e->v_con) {\n\t\t\tcontainer_set_resizing(e->v_con, true);\n\t\t\tcontainer_set_resizing(e->v_sib, true);\n\t\t\te->v_con_orig_height = e->v_con->pending.height;\n\t\t}\n\t}\n\n\tseat->seatop_impl = &seatop_impl;\n\tseat->seatop_data = e;\n\n\ttransaction_commit_dirty();\n\twlr_seat_pointer_notify_clear_focus(seat->wlr_seat);\n}\n"
  },
  {
    "path": "sway/input/switch.c",
    "content": "#include \"sway/config.h\"\n#include \"sway/input/switch.h\"\n#include \"sway/server.h\"\n#include \"log.h\"\n\nstruct sway_switch *sway_switch_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device) {\n\tstruct sway_switch *switch_device =\n\t\tcalloc(1, sizeof(struct sway_switch));\n\tif (!sway_assert(switch_device, \"could not allocate switch\")) {\n\t\treturn NULL;\n\t}\n\tdevice->switch_device = switch_device;\n\tswitch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device);\n\tswitch_device->seat_device = device;\n\tswitch_device->state = WLR_SWITCH_STATE_OFF;\n\twl_list_init(&switch_device->switch_toggle.link);\n\tsway_log(SWAY_DEBUG, \"Allocated switch for device\");\n\n\treturn switch_device;\n}\n\nstatic bool sway_switch_trigger_test(enum sway_switch_trigger trigger,\n\t\tenum wlr_switch_state state) {\n\tswitch (trigger) {\n\tcase SWAY_SWITCH_TRIGGER_ON:\n\t\treturn state == WLR_SWITCH_STATE_ON;\n\tcase SWAY_SWITCH_TRIGGER_OFF:\n\t\treturn state == WLR_SWITCH_STATE_OFF;\n\tcase SWAY_SWITCH_TRIGGER_TOGGLE:\n\t\treturn true;\n\t}\n\tabort(); // unreachable\n}\n\nstatic void execute_binding(struct sway_switch *sway_switch) {\n\tstruct sway_seat *seat = sway_switch->seat_device->sway_seat;\n\tbool locked = server.session_lock.lock;\n\n\tlist_t *bindings = config->current_mode->switch_bindings;\n\tstruct sway_switch_binding *matched_binding = NULL;\n\tfor (int i = 0; i < bindings->length; ++i) {\n\t\tstruct sway_switch_binding *binding = bindings->items[i];\n\t\tif (binding->type != sway_switch->type) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE\n\t\t\t\t|| (binding->flags & BINDING_RELOAD) == 0)) {\n\t\t\tcontinue;\n\t\t}\n\t\tbool binding_locked = binding->flags & BINDING_LOCKED;\n\t\tif (!binding_locked && locked) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tmatched_binding = binding;\n\n\t\tif (binding_locked == locked) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (matched_binding) {\n\t\tstruct sway_binding *dummy_binding =\n\t\t\tcalloc(1, sizeof(struct sway_binding));\n\t\tdummy_binding->type = BINDING_SWITCH;\n\t\tdummy_binding->flags = matched_binding->flags;\n\t\tdummy_binding->command = matched_binding->command;\n\n\t\tseat_execute_command(seat, dummy_binding);\n\t\tfree(dummy_binding);\n\t}\n}\n\nstatic void handle_switch_toggle(struct wl_listener *listener, void *data) {\n\tstruct sway_switch *sway_switch =\n\t\t\twl_container_of(listener, sway_switch, switch_toggle);\n\tstruct wlr_switch_toggle_event *event = data;\n\tstruct sway_seat *seat = sway_switch->seat_device->sway_seat;\n\tseat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH);\n\n\tstruct wlr_input_device *wlr_device =\n\t\tsway_switch->seat_device->input_device->wlr_device;\n\tchar *device_identifier = input_device_get_identifier(wlr_device);\n\tsway_log(SWAY_DEBUG, \"%s: type %d state %d\", device_identifier,\n\t\t\tevent->switch_type, event->switch_state);\n\tfree(device_identifier);\n\n\tsway_switch->type = event->switch_type;\n\tsway_switch->state = event->switch_state;\n\texecute_binding(sway_switch);\n}\n\nvoid sway_switch_configure(struct sway_switch *sway_switch) {\n\twl_list_remove(&sway_switch->switch_toggle.link);\n\twl_signal_add(&sway_switch->wlr->events.toggle,\n\t\t\t&sway_switch->switch_toggle);\n\tsway_switch->switch_toggle.notify = handle_switch_toggle;\n\tsway_log(SWAY_DEBUG, \"Configured switch for device\");\n}\n\nvoid sway_switch_destroy(struct sway_switch *sway_switch) {\n\tif (!sway_switch) {\n\t\treturn;\n\t}\n\twl_list_remove(&sway_switch->switch_toggle.link);\n\tfree(sway_switch);\n}\n\nvoid sway_switch_retrigger_bindings_for_all(void) {\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tstruct sway_seat_device *seat_device;\n\t\twl_list_for_each(seat_device, &seat->devices, link) {\n\t\t\tstruct sway_input_device *input_device = seat_device->input_device;\n\t\t\tif (input_device->wlr_device->type != WLR_INPUT_DEVICE_SWITCH) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\texecute_binding(seat_device->switch_device);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "sway/input/tablet.c",
    "content": "#include <stdlib.h>\n#include <wlr/config.h>\n#include <wlr/types/wlr_tablet_v2.h>\n#include <wlr/types/wlr_tablet_tool.h>\n#include <wlr/types/wlr_tablet_pad.h>\n#include \"log.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/input/tablet.h\"\n#include \"sway/server.h\"\n\n#if WLR_HAS_LIBINPUT_BACKEND\n#include <wlr/backend/libinput.h>\n#endif\n\nstatic void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_tablet_pad *pad =\n\t\twl_container_of(listener, pad, tablet_destroy);\n\n\tpad->tablet = NULL;\n\n\twl_list_remove(&pad->tablet_destroy.link);\n\twl_list_init(&pad->tablet_destroy.link);\n}\n\nstatic void attach_tablet_pad(struct sway_tablet_pad *tablet_pad,\n\t\tstruct sway_tablet *tablet) {\n\tsway_log(SWAY_DEBUG, \"Attaching tablet pad \\\"%s\\\" to tablet tool \\\"%s\\\"\",\n\t\ttablet_pad->seat_device->input_device->wlr_device->name,\n\t\ttablet->seat_device->input_device->wlr_device->name);\n\n\ttablet_pad->tablet = tablet;\n\n\twl_list_remove(&tablet_pad->tablet_destroy.link);\n\ttablet_pad->tablet_destroy.notify = handle_pad_tablet_destroy;\n\twl_signal_add(&tablet->seat_device->input_device->wlr_device->events.destroy,\n\t\t&tablet_pad->tablet_destroy);\n}\n\nstruct sway_tablet *sway_tablet_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device) {\n\tstruct sway_tablet *tablet =\n\t\tcalloc(1, sizeof(struct sway_tablet));\n\tif (!sway_assert(tablet, \"could not allocate sway tablet for seat\")) {\n\t\treturn NULL;\n\t}\n\n\twl_list_insert(&seat->cursor->tablets, &tablet->link);\n\n\tdevice->tablet = tablet;\n\ttablet->seat_device = device;\n\n\treturn tablet;\n}\n\nvoid sway_configure_tablet(struct sway_tablet *tablet) {\n\tstruct wlr_input_device *device =\n\t\ttablet->seat_device->input_device->wlr_device;\n\tstruct sway_seat *seat = tablet->seat_device->sway_seat;\n\n\tseat_configure_xcursor(seat);\n\n\tif (!tablet->tablet_v2) {\n\t\ttablet->tablet_v2 =\n\t\t\twlr_tablet_create(server.tablet_v2, seat->wlr_seat, device);\n\t}\n\n#if WLR_HAS_LIBINPUT_BACKEND\n\t/* Search for a sibling tablet pad */\n\tif (!wlr_input_device_is_libinput(device)) {\n\t\t/* We can only do this on libinput devices */\n\t\treturn;\n\t}\n\n\tstruct libinput_device_group *group =\n\t\tlibinput_device_get_device_group(wlr_libinput_get_device_handle(device));\n\tstruct sway_tablet_pad *tablet_pad;\n\twl_list_for_each(tablet_pad, &seat->cursor->tablet_pads, link) {\n\t\tstruct wlr_input_device *pad_device =\n\t\t\ttablet_pad->seat_device->input_device->wlr_device;\n\t\tif (!wlr_input_device_is_libinput(pad_device)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tstruct libinput_device_group *pad_group =\n\t\t\tlibinput_device_get_device_group(wlr_libinput_get_device_handle(pad_device));\n\n\t\tif (pad_group == group) {\n\t\t\tattach_tablet_pad(tablet_pad, tablet);\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n}\n\nvoid sway_tablet_destroy(struct sway_tablet *tablet) {\n\tif (!tablet) {\n\t\treturn;\n\t}\n\twl_list_remove(&tablet->link);\n\tfree(tablet);\n}\n\nstatic void handle_tablet_tool_set_cursor(struct wl_listener *listener, void *data) {\n\tstruct sway_tablet_tool *tool =\n\t\twl_container_of(listener, tool, set_cursor);\n\tstruct wlr_tablet_v2_event_cursor *event = data;\n\n\tstruct sway_cursor *cursor = tool->seat->cursor;\n\tif (!seatop_allows_set_cursor(cursor->seat)) {\n\t\treturn;\n\t}\n\n\tstruct wl_client *focused_client = NULL;\n\tstruct wlr_surface *focused_surface = tool->tablet_v2_tool->focused_surface;\n\tif (focused_surface != NULL) {\n\t\tfocused_client = wl_resource_get_client(focused_surface->resource);\n\t}\n\n\t// TODO: check cursor mode\n\tif (focused_client == NULL ||\n\t\t\tevent->seat_client->client != focused_client) {\n\t\tsway_log(SWAY_DEBUG, \"denying request to set cursor from unfocused client\");\n\t\treturn;\n\t}\n\n\tcursor_set_image_surface(cursor, event->surface, event->hotspot_x,\n\t\t\tevent->hotspot_y, focused_client);\n}\n\nstatic void handle_tablet_tool_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_tablet_tool *tool =\n\t\twl_container_of(listener, tool, tool_destroy);\n\n\twl_list_remove(&tool->tool_destroy.link);\n\twl_list_remove(&tool->set_cursor.link);\n\n\tfree(tool);\n}\n\nvoid sway_tablet_tool_configure(struct sway_tablet *tablet,\n\t\tstruct wlr_tablet_tool *wlr_tool) {\n\tstruct sway_tablet_tool *tool =\n\t\tcalloc(1, sizeof(struct sway_tablet_tool));\n\tif (!sway_assert(tool, \"could not allocate sway tablet tool for tablet\")) {\n\t\treturn;\n\t}\n\n\tswitch (wlr_tool->type) {\n\tcase WLR_TABLET_TOOL_TYPE_LENS:\n\tcase WLR_TABLET_TOOL_TYPE_MOUSE:\n\t\ttool->mode = SWAY_TABLET_TOOL_MODE_RELATIVE;\n\t\tbreak;\n\tdefault:\n\t\ttool->mode = SWAY_TABLET_TOOL_MODE_ABSOLUTE;\n\n\t\tstruct input_config *ic = input_device_get_config(\n\t\t\ttablet->seat_device->input_device);\n\t\tif (!ic) {\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (int i = 0; i < ic->tools->length; i++) {\n\t\t\tstruct input_config_tool *tool_config = ic->tools->items[i];\n\t\t\tif (tool_config->type == wlr_tool->type) {\n\t\t\t\ttool->mode = tool_config->mode;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\ttool->seat = tablet->seat_device->sway_seat;\n\ttool->tablet = tablet;\n\ttool->tablet_v2_tool =\n\t\twlr_tablet_tool_create(server.tablet_v2,\n\t\t\ttablet->seat_device->sway_seat->wlr_seat, wlr_tool);\n\n\ttool->tool_destroy.notify = handle_tablet_tool_destroy;\n\twl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy);\n\n\ttool->set_cursor.notify = handle_tablet_tool_set_cursor;\n\twl_signal_add(&tool->tablet_v2_tool->events.set_cursor,\n\t\t&tool->set_cursor);\n\n\twlr_tool->data = tool;\n}\n\nstatic void handle_tablet_pad_attach(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_tablet_pad *pad = wl_container_of(listener, pad, attach);\n\tstruct wlr_tablet_tool *wlr_tool = data;\n\tstruct sway_tablet_tool *tool = wlr_tool->data;\n\n\tif (!tool) {\n\t\treturn;\n\t}\n\n\tattach_tablet_pad(pad, tool->tablet);\n}\n\nstatic void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {\n\tstruct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);\n\tstruct wlr_tablet_pad_ring_event *event = data;\n\n\tif (!pad->current_surface) {\n\t\treturn;\n\t}\n\n\twlr_tablet_v2_tablet_pad_notify_ring(pad->tablet_v2_pad,\n\t\tevent->ring, event->position,\n\t\tevent->source == WLR_TABLET_PAD_RING_SOURCE_FINGER,\n\t\tevent->time_msec);\n}\n\nstatic void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {\n\tstruct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);\n\tstruct wlr_tablet_pad_strip_event *event = data;\n\n\tif (!pad->current_surface) {\n\t\treturn;\n\t}\n\n\twlr_tablet_v2_tablet_pad_notify_strip(pad->tablet_v2_pad,\n\t\tevent->strip, event->position,\n\t\tevent->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER,\n\t\tevent->time_msec);\n}\n\nstatic void handle_tablet_pad_button(struct wl_listener *listener, void *data) {\n\tstruct sway_tablet_pad *pad = wl_container_of(listener, pad, button);\n\tstruct wlr_tablet_pad_button_event *event = data;\n\n\tif (!pad->current_surface) {\n\t\treturn;\n\t}\n\n\twlr_tablet_v2_tablet_pad_notify_mode(pad->tablet_v2_pad,\n\t\tevent->group, event->mode, event->time_msec);\n\n\twlr_tablet_v2_tablet_pad_notify_button(pad->tablet_v2_pad,\n\t\tevent->button, event->time_msec,\n\t\t(enum zwp_tablet_pad_v2_button_state)event->state);\n}\n\nstruct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,\n\t\tstruct sway_seat_device *device) {\n\tstruct sway_tablet_pad *tablet_pad =\n\t\tcalloc(1, sizeof(struct sway_tablet_pad));\n\tif (!sway_assert(tablet_pad, \"could not allocate sway tablet\")) {\n\t\treturn NULL;\n\t}\n\n\ttablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device);\n\ttablet_pad->seat_device = device;\n\twl_list_init(&tablet_pad->attach.link);\n\twl_list_init(&tablet_pad->button.link);\n\twl_list_init(&tablet_pad->strip.link);\n\twl_list_init(&tablet_pad->ring.link);\n\twl_list_init(&tablet_pad->surface_destroy.link);\n\twl_list_init(&tablet_pad->tablet_destroy.link);\n\n\twl_list_insert(&seat->cursor->tablet_pads, &tablet_pad->link);\n\n\treturn tablet_pad;\n}\n\nvoid sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {\n\tstruct wlr_input_device *wlr_device =\n\t\ttablet_pad->seat_device->input_device->wlr_device;\n\tstruct sway_seat *seat = tablet_pad->seat_device->sway_seat;\n\n\tif (!tablet_pad->tablet_v2_pad) {\n\t\ttablet_pad->tablet_v2_pad =\n\t\t\twlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device);\n\t}\n\n\twl_list_remove(&tablet_pad->attach.link);\n\ttablet_pad->attach.notify = handle_tablet_pad_attach;\n\twl_signal_add(&tablet_pad->wlr->events.attach_tablet,\n\t\t&tablet_pad->attach);\n\n\twl_list_remove(&tablet_pad->button.link);\n\ttablet_pad->button.notify = handle_tablet_pad_button;\n\twl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button);\n\n\twl_list_remove(&tablet_pad->strip.link);\n\ttablet_pad->strip.notify = handle_tablet_pad_strip;\n\twl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip);\n\n\twl_list_remove(&tablet_pad->ring.link);\n\ttablet_pad->ring.notify = handle_tablet_pad_ring;\n\twl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);\n\n#if WLR_HAS_LIBINPUT_BACKEND\n\t/* Search for a sibling tablet */\n\tif (!wlr_input_device_is_libinput(wlr_device)) {\n\t\t/* We can only do this on libinput devices */\n\t\treturn;\n\t}\n\n\tstruct libinput_device_group *group =\n\t\tlibinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device));\n\tstruct sway_tablet *tool;\n\twl_list_for_each(tool, &seat->cursor->tablets, link) {\n\t\tstruct wlr_input_device *tablet =\n\t\t\ttool->seat_device->input_device->wlr_device;\n\t\tif (!wlr_input_device_is_libinput(tablet)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tstruct libinput_device_group *tablet_group =\n\t\t\tlibinput_device_get_device_group(wlr_libinput_get_device_handle(tablet));\n\n\t\tif (tablet_group == group) {\n\t\t\tattach_tablet_pad(tablet_pad, tool);\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n}\n\nvoid sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) {\n\tif (!tablet_pad) {\n\t\treturn;\n\t}\n\n\twl_list_remove(&tablet_pad->link);\n\twl_list_remove(&tablet_pad->attach.link);\n\twl_list_remove(&tablet_pad->button.link);\n\twl_list_remove(&tablet_pad->strip.link);\n\twl_list_remove(&tablet_pad->ring.link);\n\twl_list_remove(&tablet_pad->surface_destroy.link);\n\twl_list_remove(&tablet_pad->tablet_destroy.link);\n\n\tfree(tablet_pad);\n}\n\nstatic void handle_pad_tablet_surface_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_tablet_pad *tablet_pad =\n\t\twl_container_of(listener, tablet_pad, surface_destroy);\n\n\tsway_tablet_pad_set_focus(tablet_pad, NULL);\n}\n\nvoid sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,\n\t\tstruct wlr_surface *surface) {\n\tif (!tablet_pad || !tablet_pad->tablet) {\n\t\treturn;\n\t}\n\n\tif (surface == tablet_pad->current_surface) {\n\t\treturn;\n\t}\n\n\t/* Leave current surface */\n\tif (tablet_pad->current_surface) {\n\t\twlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad,\n\t\t\ttablet_pad->current_surface);\n\t\twl_list_remove(&tablet_pad->surface_destroy.link);\n\t\twl_list_init(&tablet_pad->surface_destroy.link);\n\t\ttablet_pad->current_surface = NULL;\n\t}\n\n\tif (surface == NULL ||\n\t\t\t!wlr_surface_accepts_tablet_v2(surface, tablet_pad->tablet->tablet_v2)) {\n\t\treturn;\n\t}\n\n\twlr_tablet_v2_tablet_pad_notify_enter(tablet_pad->tablet_v2_pad,\n\t\ttablet_pad->tablet->tablet_v2, surface);\n\n\ttablet_pad->current_surface = surface;\n\ttablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy;\n\twl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);\n}\n"
  },
  {
    "path": "sway/input/text_input.c",
    "content": "#include <assert.h>\n#include <stdlib.h>\n#include \"log.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/output.h\"\n#include \"sway/input/text_input.h\"\n#include \"sway/input/text_input_popup.h\"\n#include \"sway/layers.h\"\n#include \"sway/server.h\"\n#include <wlr/types/wlr_session_lock_v1.h>\n\nstatic struct sway_text_input *relay_get_focusable_text_input(\n\t\tstruct sway_input_method_relay *relay) {\n\tstruct sway_text_input *text_input = NULL;\n\twl_list_for_each(text_input, &relay->text_inputs, link) {\n\t\tif (text_input->pending_focused_surface) {\n\t\t\treturn text_input;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic struct sway_text_input *relay_get_focused_text_input(\n\t\tstruct sway_input_method_relay *relay) {\n\tstruct sway_text_input *text_input = NULL;\n\twl_list_for_each(text_input, &relay->text_inputs, link) {\n\t\tif (text_input->input->focused_surface) {\n\t\t\treturn text_input;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void handle_im_commit(struct wl_listener *listener, void *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\tinput_method_commit);\n\n\tstruct sway_text_input *text_input = relay_get_focused_text_input(relay);\n\tif (!text_input) {\n\t\treturn;\n\t}\n\tif (relay->input_method->current.preedit.text) {\n\t\twlr_text_input_v3_send_preedit_string(text_input->input,\n\t\t\trelay->input_method->current.preedit.text,\n\t\t\trelay->input_method->current.preedit.cursor_begin,\n\t\t\trelay->input_method->current.preedit.cursor_end);\n\t}\n\tif (relay->input_method->current.commit_text) {\n\t\twlr_text_input_v3_send_commit_string(text_input->input,\n\t\t\trelay->input_method->current.commit_text);\n\t}\n\tif (relay->input_method->current.delete.before_length\n\t\t\t|| relay->input_method->current.delete.after_length) {\n\t\twlr_text_input_v3_send_delete_surrounding_text(text_input->input,\n\t\t\trelay->input_method->current.delete.before_length,\n\t\t\trelay->input_method->current.delete.after_length);\n\t}\n\twlr_text_input_v3_send_done(text_input->input);\n}\n\nstatic void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\tinput_method_keyboard_grab_destroy);\n\tstruct wlr_input_method_keyboard_grab_v2 *keyboard_grab = relay->input_method->keyboard_grab;\n\tstruct wlr_seat *wlr_seat = keyboard_grab->input_method->seat;\n\twl_list_remove(&relay->input_method_keyboard_grab_destroy.link);\n\n\tif (keyboard_grab->keyboard) {\n\t\t// send modifier state to original client\n\t\twlr_seat_set_keyboard(wlr_seat, keyboard_grab->keyboard);\n\t\twlr_seat_keyboard_notify_modifiers(wlr_seat,\n\t\t\t&keyboard_grab->keyboard->modifiers);\n\t}\n}\n\nstatic void handle_im_grab_keyboard(struct wl_listener *listener, void *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\tinput_method_grab_keyboard);\n\tstruct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;\n\n\t// send modifier state to grab\n\tstruct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat);\n\twlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,\n\t\tactive_keyboard);\n\n\twl_signal_add(&keyboard_grab->events.destroy,\n\t\t&relay->input_method_keyboard_grab_destroy);\n\trelay->input_method_keyboard_grab_destroy.notify =\n\t\thandle_im_keyboard_grab_destroy;\n}\n\nstatic void text_input_set_pending_focused_surface(\n\t\tstruct sway_text_input *text_input, struct wlr_surface *surface) {\n\twl_list_remove(&text_input->pending_focused_surface_destroy.link);\n\ttext_input->pending_focused_surface = surface;\n\n\tif (surface) {\n\t\twl_signal_add(&surface->events.destroy,\n\t\t\t&text_input->pending_focused_surface_destroy);\n\t} else {\n\t\twl_list_init(&text_input->pending_focused_surface_destroy.link);\n\t}\n}\n\nstatic void handle_im_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\tinput_method_destroy);\n\twl_list_remove(&relay->input_method_commit.link);\n\twl_list_remove(&relay->input_method_grab_keyboard.link);\n\twl_list_remove(&relay->input_method_destroy.link);\n\twl_list_remove(&relay->input_method_new_popup_surface.link);\n\trelay->input_method = NULL;\n\tstruct sway_text_input *text_input = relay_get_focused_text_input(relay);\n\tif (text_input) {\n\t\t// keyboard focus is still there, so keep the surface at hand in case\n\t\t// the input method returns\n\t\ttext_input_set_pending_focused_surface(text_input,\n\t\t\ttext_input->input->focused_surface);\n\t\twlr_text_input_v3_send_leave(text_input->input);\n\t}\n}\n\nstatic void constrain_popup(struct sway_input_popup *popup) {\n\tstruct sway_text_input *text_input =\n\t\trelay_get_focused_text_input(popup->relay);\n\n\tif (!popup->desc.relative) {\n\t\treturn;\n\t}\n\n\tstruct wlr_box parent = {0};\n\twlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y);\n\n\tstruct wlr_box geo = {0};\n\tstruct wlr_output *output;\n\n\tif (popup->desc.view) {\n\t\tstruct sway_view *view = popup->desc.view;\n\t\toutput = wlr_output_layout_output_at(root->output_layout,\n\t\t\tview->container->pending.content_x + view->geometry.x,\n\t\t\tview->container->pending.content_y + view->geometry.y);\n\n\t\tparent.width = view->geometry.width;\n\t\tparent.height = view->geometry.height;\n\t\tgeo = view->geometry;\n\t} else {\n\t\toutput = popup->fixed_output;\n\t}\n\n\tstruct wlr_box output_box;\n\twlr_output_layout_get_box(root->output_layout, output, &output_box);\n\n\tbool cursor_rect = text_input->input->current.features &\n\t\tWLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;\n\tstruct wlr_box cursor_area;\n\tif (cursor_rect) {\n\t\tcursor_area = text_input->input->current.cursor_rectangle;\n\t} else {\n\t\tcursor_area = (struct wlr_box) {\n\t\t\t.width = parent.width,\n\t\t\t.height = parent.height,\n\t\t};\n\t}\n\n\tint popup_width = popup->popup_surface->surface->current.width;\n\tint popup_height = popup->popup_surface->surface->current.height;\n\tint x1 = parent.x + cursor_area.x;\n\tint x2 = parent.x + cursor_area.x + cursor_area.width;\n\tint y1 = parent.y + cursor_area.y;\n\tint y2 = parent.y + cursor_area.y + cursor_area.height;\n\tint x = x1;\n\tint y = y2;\n\n\tint available_right = output_box.x + output_box.width - x1;\n\tint available_left = x2 - output_box.x;\n\tif (available_right < popup_width && available_left > available_right) {\n\t\tx = x2 - popup_width;\n\t}\n\n\tint available_down = output_box.y + output_box.height - y2;\n\tint available_up = y1 - output_box.y;\n\tif (available_down < popup_height && available_up > available_down) {\n\t\ty = y1 - popup_height;\n\t}\n\n\twlr_scene_node_set_position(popup->desc.relative, x - parent.x - geo.x, y - parent.y - geo.y);\n\tif (cursor_rect) {\n\t\tstruct wlr_box box = {\n\t\t\t.x = x1 - x,\n\t\t\t.y = y1 - y,\n\t\t\t.width = cursor_area.width,\n\t\t\t.height = cursor_area.height,\n\t\t};\n\t\twlr_input_popup_surface_v2_send_text_input_rectangle(\n\t\t\tpopup->popup_surface, &box);\n\t}\n\n\tif (popup->scene_tree) {\n\t\twlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y);\n\t}\n}\n\nstatic void input_popup_set_focus(struct sway_input_popup *popup,\n\t\tstruct wlr_surface *surface);\n\nstatic void relay_send_im_state(struct sway_input_method_relay *relay,\n\t\tstruct wlr_text_input_v3 *input) {\n\tstruct wlr_input_method_v2 *input_method = relay->input_method;\n\tif (!input_method) {\n\t\tsway_log(SWAY_INFO, \"Sending IM_DONE but im is gone\");\n\t\treturn;\n\t}\n\t// TODO: only send each of those if they were modified\n\tif (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) {\n\t\twlr_input_method_v2_send_surrounding_text(input_method,\n\t\t\tinput->current.surrounding.text, input->current.surrounding.cursor,\n\t\t\tinput->current.surrounding.anchor);\n\t}\n\twlr_input_method_v2_send_text_change_cause(input_method,\n\t\tinput->current.text_change_cause);\n\tif (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) {\n\t\twlr_input_method_v2_send_content_type(input_method,\n\t\t\tinput->current.content_type.hint,\n\t\t\tinput->current.content_type.purpose);\n\t}\n\n\tstruct sway_text_input *text_input = relay_get_focused_text_input(relay);\n\n\tstruct sway_input_popup *popup;\n\twl_list_for_each(popup, &relay->input_popups, link) {\n\t\tif (text_input != NULL) {\n\t\t\tinput_popup_set_focus(popup, text_input->input->focused_surface);\n\t\t} else {\n\t\t\tinput_popup_set_focus(popup, NULL);\n\t\t}\n\t}\n\twlr_input_method_v2_send_done(input_method);\n\t// TODO: pass intent, display popup size\n}\n\nstatic void handle_text_input_enable(struct wl_listener *listener, void *data) {\n\tstruct sway_text_input *text_input = wl_container_of(listener, text_input,\n\t\ttext_input_enable);\n\tif (text_input->input->focused_surface == NULL) {\n\t\tsway_log(SWAY_DEBUG, \"Enabling text input, but no longer focused\");\n\t\treturn;\n\t}\n\tif (text_input->relay->input_method == NULL) {\n\t\tsway_log(SWAY_INFO, \"Enabling text input when input method is gone\");\n\t\treturn;\n\t}\n\twlr_input_method_v2_send_activate(text_input->relay->input_method);\n\trelay_send_im_state(text_input->relay, text_input->input);\n}\n\nstatic void handle_text_input_commit(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_text_input *text_input = wl_container_of(listener, text_input,\n\t\ttext_input_commit);\n\tif (text_input->input->focused_surface == NULL) {\n\t\tsway_log(SWAY_DEBUG, \"Unfocused text input tried to commit an update\");\n\t\treturn;\n\t}\n\tif (!text_input->input->current_enabled) {\n\t\tsway_log(SWAY_INFO, \"Inactive text input tried to commit an update\");\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Text input committed update\");\n\tif (text_input->relay->input_method == NULL) {\n\t\tsway_log(SWAY_INFO, \"Text input committed, but input method is gone\");\n\t\treturn;\n\t}\n\trelay_send_im_state(text_input->relay, text_input->input);\n}\n\nstatic void relay_disable_text_input(struct sway_input_method_relay *relay,\n\t\tstruct sway_text_input *text_input) {\n\tif (relay->input_method == NULL) {\n\t\tsway_log(SWAY_DEBUG, \"Disabling text input, but input method is gone\");\n\t\treturn;\n\t}\n\twlr_input_method_v2_send_deactivate(relay->input_method);\n\trelay_send_im_state(relay, text_input->input);\n}\n\nstatic void handle_text_input_disable(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_text_input *text_input = wl_container_of(listener, text_input,\n\t\ttext_input_disable);\n\tif (text_input->input->focused_surface == NULL) {\n\t\tsway_log(SWAY_DEBUG, \"Disabling text input, but no longer focused\");\n\t\treturn;\n\t}\n\trelay_disable_text_input(text_input->relay, text_input);\n}\n\nstatic void handle_text_input_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_text_input *text_input = wl_container_of(listener, text_input,\n\t\ttext_input_destroy);\n\n\tif (text_input->input->current_enabled) {\n\t\trelay_disable_text_input(text_input->relay, text_input);\n\t}\n\ttext_input_set_pending_focused_surface(text_input, NULL);\n\twl_list_remove(&text_input->text_input_commit.link);\n\twl_list_remove(&text_input->text_input_destroy.link);\n\twl_list_remove(&text_input->text_input_disable.link);\n\twl_list_remove(&text_input->text_input_enable.link);\n\twl_list_remove(&text_input->link);\n\tfree(text_input);\n}\n\nstatic void handle_pending_focused_surface_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_text_input *text_input = wl_container_of(listener, text_input,\n\t\tpending_focused_surface_destroy);\n\ttext_input->pending_focused_surface = NULL;\n\twl_list_remove(&text_input->pending_focused_surface_destroy.link);\n\twl_list_init(&text_input->pending_focused_surface_destroy.link);\n}\n\nstruct sway_text_input *sway_text_input_create(\n\t\tstruct sway_input_method_relay *relay,\n\t\tstruct wlr_text_input_v3 *text_input) {\n\tstruct sway_text_input *input = calloc(1, sizeof(struct sway_text_input));\n\tif (!input) {\n\t\treturn NULL;\n\t}\n\tinput->input = text_input;\n\tinput->relay = relay;\n\n\twl_list_insert(&relay->text_inputs, &input->link);\n\n\tinput->text_input_enable.notify = handle_text_input_enable;\n\twl_signal_add(&text_input->events.enable, &input->text_input_enable);\n\n\tinput->text_input_commit.notify = handle_text_input_commit;\n\twl_signal_add(&text_input->events.commit, &input->text_input_commit);\n\n\tinput->text_input_disable.notify = handle_text_input_disable;\n\twl_signal_add(&text_input->events.disable, &input->text_input_disable);\n\n\tinput->text_input_destroy.notify = handle_text_input_destroy;\n\twl_signal_add(&text_input->events.destroy, &input->text_input_destroy);\n\n\tinput->pending_focused_surface_destroy.notify =\n\t\thandle_pending_focused_surface_destroy;\n\twl_list_init(&input->pending_focused_surface_destroy.link);\n\treturn input;\n}\n\nstatic void relay_handle_text_input(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\ttext_input_new);\n\tstruct wlr_text_input_v3 *wlr_text_input = data;\n\tif (relay->seat->wlr_seat != wlr_text_input->seat) {\n\t\treturn;\n\t}\n\n\tsway_text_input_create(relay, wlr_text_input);\n}\n\nstatic void input_popup_set_focus(struct sway_input_popup *popup,\n\t\tstruct wlr_surface *surface) {\n\twl_list_remove(&popup->focused_surface_unmap.link);\n\n\tif (!popup->scene_tree) {\n\t\twl_list_init(&popup->focused_surface_unmap.link);\n\t\treturn;\n\t}\n\n\tif (popup->desc.relative) {\n\t\tscene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP);\n\t\twlr_scene_node_destroy(popup->desc.relative);\n\t\tpopup->desc.relative = NULL;\n\t}\n\n\tif (surface == NULL) {\n\t\twl_list_init(&popup->focused_surface_unmap.link);\n\t\twlr_scene_node_set_enabled(&popup->scene_tree->node, false);\n\t\treturn;\n\t}\n\n\tstruct wlr_layer_surface_v1 *layer_surface =\n\t\twlr_layer_surface_v1_try_from_wlr_surface(surface);\n\tstruct wlr_session_lock_surface_v1 *lock_surface =\n\t\twlr_session_lock_surface_v1_try_from_wlr_surface(surface);\n\n\tstruct wlr_scene_tree *relative_parent;\n\tif (layer_surface) {\n\t\twl_signal_add(&layer_surface->surface->events.unmap,\n\t\t\t&popup->focused_surface_unmap);\n\n\t\tstruct sway_layer_surface *layer = layer_surface->data;\n\t\tif (layer == NULL) {\n\t\t\treturn;\n\t\t}\n\n\t\trelative_parent = layer->scene->tree;\n\t\tpopup->desc.view = NULL;\n\n\t\t// we don't need to add an event here to NULL out this field because\n\t\t// this field will only be initialized if the popup is part of a layer\n\t\t// surface. Layer surfaces get destroyed as part of the output being\n\t\t// destroyed, thus also trickling down to popups.\n\t\tpopup->fixed_output = layer->layer_surface->output;\n\t} else if (lock_surface) {\n\t\twl_signal_add(&lock_surface->surface->events.unmap,\n\t\t\t&popup->focused_surface_unmap);\n\n\t\tstruct sway_layer_surface *lock = lock_surface->data;\n\t\tif (lock == NULL) {\n\t\t\treturn;\n\t\t}\n\n\t\trelative_parent = lock->scene->tree;\n\t\tpopup->desc.view = NULL;\n\n\t\t// we don't need to add an event here to NULL out this field because\n\t\t// this field will only be initialized if the popup is part of a layer\n\t\t// surface. Layer surfaces get destroyed as part of the output being\n\t\t// destroyed, thus also trickling down to popups.\n\t\tpopup->fixed_output = lock->layer_surface->output;\n\t} else {\n\t\tstruct sway_view *view = view_from_wlr_surface(surface);\n\t\t// In the future there may be other shells been added, so we also need to check here.\n\t\tif (view == NULL) {\n\t\t\tsway_log(SWAY_DEBUG, \"Unsupported IME focus surface\");\n\t\t\treturn;\n\t\t}\n\t\twl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);\n\t\trelative_parent = view->scene_tree;\n\t\tpopup->desc.view = view;\n\t}\n\n\tstruct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent);\n\n\tpopup->desc.relative = &relative->node;\n\tif (!scene_descriptor_assign(&popup->scene_tree->node,\n\t\t\tSWAY_SCENE_DESC_POPUP, &popup->desc)) {\n\t\twlr_scene_node_destroy(&popup->scene_tree->node);\n\t\tpopup->scene_tree = NULL;\n\t\treturn;\n\t}\n\n\tconstrain_popup(popup);\n\twlr_scene_node_set_enabled(&popup->scene_tree->node, true);\n}\n\nstatic void handle_im_popup_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_input_popup *popup =\n\t\twl_container_of(listener, popup, popup_destroy);\n\twlr_scene_node_destroy(&popup->scene_tree->node);\n\twl_list_remove(&popup->focused_surface_unmap.link);\n\twl_list_remove(&popup->popup_surface_commit.link);\n\twl_list_remove(&popup->popup_surface_map.link);\n\twl_list_remove(&popup->popup_surface_unmap.link);\n\twl_list_remove(&popup->popup_destroy.link);\n\twl_list_remove(&popup->link);\n\n\tfree(popup);\n}\n\nstatic void handle_im_popup_surface_map(struct wl_listener *listener, void *data) {\n\tstruct sway_input_popup *popup =\n\t\twl_container_of(listener, popup, popup_surface_map);\n\tstruct sway_text_input *text_input = relay_get_focused_text_input(popup->relay);\n\tif (text_input != NULL) {\n\t\tinput_popup_set_focus(popup, text_input->input->focused_surface);\n\t} else {\n\t\tinput_popup_set_focus(popup, NULL);\n\t}\n}\n\nstatic void handle_im_popup_surface_unmap(struct wl_listener *listener, void *data) {\n\tstruct sway_input_popup *popup =\n\t\twl_container_of(listener, popup, popup_surface_unmap);\n\n\tscene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP);\n\t// relative should already be freed as it should be a child of the just unmapped scene\n\tpopup->desc.relative = NULL;\n\n\tinput_popup_set_focus(popup, NULL);\n}\n\nstatic void handle_im_popup_surface_commit(struct wl_listener *listener, void *data) {\n\tstruct sway_input_popup *popup =\n\t\twl_container_of(listener, popup, popup_surface_commit);\n\n\tconstrain_popup(popup);\n}\n\nstatic void handle_im_focused_surface_unmap(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_input_popup *popup =\n\t\twl_container_of(listener, popup, focused_surface_unmap);\n\n\tinput_popup_set_focus(popup, NULL);\n}\n\nstatic void handle_im_new_popup_surface(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\tinput_method_new_popup_surface);\n\tstruct sway_input_popup *popup = calloc(1, sizeof(*popup));\n\tif (!popup) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate an input method popup\");\n\t\treturn;\n\t}\n\n\tpopup->relay = relay;\n\tpopup->popup_surface = data;\n\tpopup->popup_surface->data = popup;\n\n\tpopup->scene_tree = wlr_scene_tree_create(root->layers.popup);\n\tif (!popup->scene_tree) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate scene tree\");\n\t\tfree(popup);\n\t\treturn;\n\t}\n\n\tif (!wlr_scene_subsurface_tree_create(popup->scene_tree,\n\t\t\tpopup->popup_surface->surface)) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate subsurface tree\");\n\t\twlr_scene_node_destroy(&popup->scene_tree->node);\n\t\tfree(popup);\n\t\treturn;\n\t}\n\n\twl_signal_add(&popup->popup_surface->events.destroy, &popup->popup_destroy);\n\tpopup->popup_destroy.notify = handle_im_popup_destroy;\n\twl_signal_add(&popup->popup_surface->surface->events.commit, &popup->popup_surface_commit);\n\tpopup->popup_surface_commit.notify = handle_im_popup_surface_commit;\n\twl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_surface_map);\n\tpopup->popup_surface_map.notify = handle_im_popup_surface_map;\n\twl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->popup_surface_unmap);\n\tpopup->popup_surface_unmap.notify = handle_im_popup_surface_unmap;\n\twl_list_init(&popup->focused_surface_unmap.link);\n\tpopup->focused_surface_unmap.notify = handle_im_focused_surface_unmap;\n\n\tstruct sway_text_input *text_input = relay_get_focused_text_input(relay);\n\tif (text_input != NULL) {\n\t\tinput_popup_set_focus(popup, text_input->input->focused_surface);\n\t} else {\n\t\tinput_popup_set_focus(popup, NULL);\n\t}\n\n\twl_list_insert(&relay->input_popups, &popup->link);\n}\n\nstatic void text_input_send_enter(struct sway_text_input *text_input,\n\t\tstruct wlr_surface *surface) {\n\twlr_text_input_v3_send_enter(text_input->input, surface);\n\tstruct sway_input_popup *popup;\n\twl_list_for_each(popup, &text_input->relay->input_popups, link) {\n\t\tinput_popup_set_focus(popup, surface);\n\t}\n}\n\nstatic void relay_handle_input_method(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\tinput_method_new);\n\tstruct wlr_input_method_v2 *input_method = data;\n\tif (relay->seat->wlr_seat != input_method->seat) {\n\t\treturn;\n\t}\n\n\tif (relay->input_method != NULL) {\n\t\tsway_log(SWAY_INFO, \"Attempted to connect second input method to a seat\");\n\t\twlr_input_method_v2_send_unavailable(input_method);\n\t\treturn;\n\t}\n\n\trelay->input_method = input_method;\n\twl_signal_add(&relay->input_method->events.commit,\n\t\t&relay->input_method_commit);\n\trelay->input_method_commit.notify = handle_im_commit;\n\twl_signal_add(&relay->input_method->events.grab_keyboard,\n\t\t&relay->input_method_grab_keyboard);\n\trelay->input_method_grab_keyboard.notify = handle_im_grab_keyboard;\n\twl_signal_add(&relay->input_method->events.destroy,\n\t\t&relay->input_method_destroy);\n\trelay->input_method_destroy.notify = handle_im_destroy;\n\twl_signal_add(&relay->input_method->events.new_popup_surface,\n\t\t&relay->input_method_new_popup_surface);\n\trelay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;\n\n\tstruct sway_text_input *text_input = relay_get_focusable_text_input(relay);\n\tif (text_input) {\n\t\ttext_input_send_enter(text_input,\n\t\t\ttext_input->pending_focused_surface);\n\t\ttext_input_set_pending_focused_surface(text_input, NULL);\n\t}\n}\n\nstatic void sway_input_method_relay_finish_text_input(struct sway_input_method_relay *relay) {\n\twl_list_remove(&relay->text_input_new.link);\n\twl_list_remove(&relay->text_input_manager_destroy.link);\n\twl_list_init(&relay->text_input_new.link);\n\twl_list_init(&relay->text_input_manager_destroy.link);\n}\n\nstatic void relay_handle_text_input_manager_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\ttext_input_manager_destroy);\n\n\tsway_input_method_relay_finish_text_input(relay);\n}\n\nstatic void sway_input_method_relay_finish_input_method(struct sway_input_method_relay *relay) {\n\twl_list_remove(&relay->input_method_new.link);\n\twl_list_remove(&relay->input_method_manager_destroy.link);\n\twl_list_init(&relay->input_method_new.link);\n\twl_list_init(&relay->input_method_manager_destroy.link);\n}\n\nstatic void relay_handle_input_method_manager_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_input_method_relay *relay = wl_container_of(listener, relay,\n\t\tinput_method_manager_destroy);\n\n\tsway_input_method_relay_finish_input_method(relay);\n}\n\nvoid sway_input_method_relay_init(struct sway_seat *seat,\n\t\tstruct sway_input_method_relay *relay) {\n\trelay->seat = seat;\n\twl_list_init(&relay->text_inputs);\n\twl_list_init(&relay->input_popups);\n\n\trelay->text_input_new.notify = relay_handle_text_input;\n\twl_signal_add(&server.text_input->events.new_text_input,\n\t\t&relay->text_input_new);\n\trelay->text_input_manager_destroy.notify = relay_handle_text_input_manager_destroy;\n\twl_signal_add(&server.text_input->events.destroy,\n\t\t&relay->text_input_manager_destroy);\n\n\trelay->input_method_new.notify = relay_handle_input_method;\n\twl_signal_add(\n\t\t&server.input_method->events.new_input_method,\n\t\t&relay->input_method_new);\n\trelay->input_method_manager_destroy.notify = relay_handle_input_method_manager_destroy;\n\twl_signal_add(&server.input_method->events.destroy,\n\t\t&relay->input_method_manager_destroy);\n}\n\nvoid sway_input_method_relay_finish(struct sway_input_method_relay *relay) {\n\tsway_input_method_relay_finish_text_input(relay);\n\tsway_input_method_relay_finish_input_method(relay);\n}\n\nvoid sway_input_method_relay_set_focus(struct sway_input_method_relay *relay,\n\t\tstruct wlr_surface *surface) {\n\tstruct sway_text_input *text_input;\n\twl_list_for_each(text_input, &relay->text_inputs, link) {\n\t\tif (text_input->pending_focused_surface) {\n\t\t\tassert(text_input->input->focused_surface == NULL);\n\t\t\tif (surface != text_input->pending_focused_surface) {\n\t\t\t\ttext_input_set_pending_focused_surface(text_input, NULL);\n\t\t\t}\n\t\t} else if (text_input->input->focused_surface) {\n\t\t\tassert(text_input->pending_focused_surface == NULL);\n\t\t\tif (surface != text_input->input->focused_surface) {\n\t\t\t\trelay_disable_text_input(relay, text_input);\n\t\t\t\twlr_text_input_v3_send_leave(text_input->input);\n\t\t\t} else {\n\t\t\t\tsway_log(SWAY_DEBUG, \"IM relay set_focus already focused\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (surface\n\t\t\t\t&& wl_resource_get_client(text_input->input->resource)\n\t\t\t\t== wl_resource_get_client(surface->resource)) {\n\t\t\tif (relay->input_method) {\n\t\t\t\twlr_text_input_v3_send_enter(text_input->input, surface);\n\t\t\t} else {\n\t\t\t\ttext_input_set_pending_focused_surface(text_input, surface);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sway/ipc-json.c",
    "content": "#include <ctype.h>\n#include <float.h>\n#include <json.h>\n#include <libevdev/libevdev.h>\n#include <stdio.h>\n#include <wlr/config.h>\n#include <wlr/types/wlr_content_type_v1.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>\n#include <xkbcommon/xkbcommon.h>\n#include \"config.h\"\n#include \"log.h\"\n#include \"sway/config.h\"\n#include \"sway/ipc-json.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/output.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"wlr-layer-shell-unstable-v1-protocol.h\"\n#include \"sway/desktop/idle_inhibit_v1.h\"\n\n#if WLR_HAS_LIBINPUT_BACKEND\n#include <wlr/backend/libinput.h>\n#endif\n\nstatic const int i3_output_id = INT32_MAX;\nstatic const int i3_scratch_id = INT32_MAX - 1;\n\nstatic const char *ipc_json_node_type_description(enum sway_node_type node_type) {\n\tswitch (node_type) {\n\tcase N_ROOT:\n\t\treturn \"root\";\n\tcase N_OUTPUT:\n\t\treturn \"output\";\n\tcase N_WORKSPACE:\n\t\treturn \"workspace\";\n\tcase N_CONTAINER:\n\t\treturn \"con\";\n\t}\n\treturn \"none\";\n}\n\nstatic const char *ipc_json_layout_description(enum sway_container_layout l) {\n\tswitch (l) {\n\tcase L_VERT:\n\t\treturn \"splitv\";\n\tcase L_HORIZ:\n\t\treturn \"splith\";\n\tcase L_TABBED:\n\t\treturn \"tabbed\";\n\tcase L_STACKED:\n\t\treturn \"stacked\";\n\tcase L_NONE:\n\t\tbreak;\n\t}\n\treturn \"none\";\n}\n\nstatic const char *ipc_json_orientation_description(enum sway_container_layout l) {\n\tswitch (l) {\n\tcase L_VERT:\n\t\treturn \"vertical\";\n\tcase L_HORIZ:\n\t\treturn \"horizontal\";\n\tdefault:\n\t\treturn \"none\";\n\t}\n}\n\nstatic const char *ipc_json_border_description(enum sway_container_border border) {\n\tswitch (border) {\n\tcase B_NONE:\n\t\treturn \"none\";\n\tcase B_PIXEL:\n\t\treturn \"pixel\";\n\tcase B_NORMAL:\n\t\treturn \"normal\";\n\tcase B_CSD:\n\t\treturn \"csd\";\n\t}\n\treturn \"unknown\";\n}\n\nstatic const char *ipc_json_output_transform_description(enum wl_output_transform transform) {\n\tswitch (transform) {\n\tcase WL_OUTPUT_TRANSFORM_NORMAL:\n\t\treturn \"normal\";\n\tcase WL_OUTPUT_TRANSFORM_90:\n\t\t// Sway uses clockwise transforms, while WL_OUTPUT_TRANSFORM_* describes\n\t\t// anti-clockwise transforms.\n\t\treturn \"270\";\n\tcase WL_OUTPUT_TRANSFORM_180:\n\t\treturn \"180\";\n\tcase WL_OUTPUT_TRANSFORM_270:\n\t\t// Transform also inverted here.\n\t\treturn \"90\";\n\tcase WL_OUTPUT_TRANSFORM_FLIPPED:\n\t\treturn \"flipped\";\n\tcase WL_OUTPUT_TRANSFORM_FLIPPED_90:\n\t\t// Inverted.\n\t\treturn \"flipped-270\";\n\tcase WL_OUTPUT_TRANSFORM_FLIPPED_180:\n\t\treturn \"flipped-180\";\n\tcase WL_OUTPUT_TRANSFORM_FLIPPED_270:\n\t\t// Inverted.\n\t\treturn \"flipped-90\";\n\t}\n\treturn NULL;\n}\n\nstatic const char *ipc_json_output_adaptive_sync_status_description(\n\t\tenum wlr_output_adaptive_sync_status status) {\n\tswitch (status) {\n\tcase WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED:\n\t\treturn \"disabled\";\n\tcase WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED:\n\t\treturn \"enabled\";\n\t}\n\treturn NULL;\n}\n\nstatic const char *ipc_json_output_mode_aspect_ratio_description(\n\t\tenum wlr_output_mode_aspect_ratio aspect_ratio) {\n\tswitch (aspect_ratio) {\n\tcase WLR_OUTPUT_MODE_ASPECT_RATIO_NONE:\n\t\treturn \"none\";\n\tcase WLR_OUTPUT_MODE_ASPECT_RATIO_4_3:\n\t\treturn \"4:3\";\n\tcase WLR_OUTPUT_MODE_ASPECT_RATIO_16_9:\n\t\treturn \"16:9\";\n\tcase WLR_OUTPUT_MODE_ASPECT_RATIO_64_27:\n\t\treturn \"64:27\";\n\tcase WLR_OUTPUT_MODE_ASPECT_RATIO_256_135:\n\t\treturn \"256:135\";\n\t}\n\treturn NULL;\n}\n\nstatic json_object *ipc_json_output_mode_description(\n\t\tconst struct wlr_output_mode *mode) {\n\tconst char *pic_ar =\n\t\tipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio);\n\tjson_object *mode_object = json_object_new_object();\n\tjson_object_object_add(mode_object, \"width\",\n\t\tjson_object_new_int(mode->width));\n\tjson_object_object_add(mode_object, \"height\",\n\t\tjson_object_new_int(mode->height));\n\tjson_object_object_add(mode_object, \"refresh\",\n\t\tjson_object_new_int(mode->refresh));\n\tjson_object_object_add(mode_object, \"picture_aspect_ratio\",\n\t\tjson_object_new_string(pic_ar));\n\treturn mode_object;\n}\n\n#if WLR_HAS_XWAYLAND\nstatic const char *ipc_json_xwindow_type_description(struct sway_view *view) {\n\tstruct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;\n\tstruct sway_xwayland *xwayland = &server.xwayland;\n\n\tfor (size_t i = 0; i < surface->window_type_len; ++i) {\n\t\txcb_atom_t type = surface->window_type[i];\n\t\tif (type == xwayland->atoms[NET_WM_WINDOW_TYPE_NORMAL]) {\n\t\t\treturn \"normal\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_DIALOG]) {\n\t\t\treturn \"dialog\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_UTILITY]) {\n\t\t\treturn \"utility\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_TOOLBAR]) {\n\t\t\treturn \"toolbar\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_SPLASH]) {\n\t\t\treturn \"splash\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_MENU]) {\n\t\t\treturn \"menu\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_DROPDOWN_MENU]) {\n\t\t\treturn \"dropdown_menu\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_POPUP_MENU]) {\n\t\t\treturn \"popup_menu\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_TOOLTIP]) {\n\t\t\treturn \"tooltip\";\n\t\t} else if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_NOTIFICATION]) {\n\t\t\treturn \"notification\";\n\t\t} else {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\treturn \"unknown\";\n}\n#endif\n\nstatic const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhibit_mode mode) {\n\tswitch (mode) {\n\tcase INHIBIT_IDLE_FOCUS:\n\t\treturn \"focus\";\n\tcase INHIBIT_IDLE_FULLSCREEN:\n\t\treturn \"fullscreen\";\n\tcase INHIBIT_IDLE_OPEN:\n\t\treturn \"open\";\n\tcase INHIBIT_IDLE_VISIBLE:\n\t\treturn \"visible\";\n\tcase INHIBIT_IDLE_APPLICATION:\n\t\treturn NULL;\n\t}\n\treturn NULL;\n}\n\nstatic const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) {\n\tswitch (type) {\n\tcase WP_CONTENT_TYPE_V1_TYPE_NONE:\n\t\treturn \"none\";\n\tcase WP_CONTENT_TYPE_V1_TYPE_PHOTO:\n\t\treturn \"photo\";\n\tcase WP_CONTENT_TYPE_V1_TYPE_VIDEO:\n\t\treturn \"video\";\n\tcase WP_CONTENT_TYPE_V1_TYPE_GAME:\n\t\treturn \"game\";\n\t}\n\treturn NULL;\n}\n\njson_object *ipc_json_get_version(void) {\n\tint major = 0, minor = 0, patch = 0;\n\tjson_object *version = json_object_new_object();\n\n\tsscanf(SWAY_VERSION, \"%d.%d.%d\", &major, &minor, &patch);\n\n\tjson_object_object_add(version, \"human_readable\", json_object_new_string(SWAY_VERSION));\n\tjson_object_object_add(version, \"variant\", json_object_new_string(\"sway\"));\n\tjson_object_object_add(version, \"major\", json_object_new_int(major));\n\tjson_object_object_add(version, \"minor\", json_object_new_int(minor));\n\tjson_object_object_add(version, \"patch\", json_object_new_int(patch));\n\tjson_object_object_add(version, \"loaded_config_file_name\", json_object_new_string(config->current_config_path));\n\n\treturn version;\n}\n\nstatic json_object *ipc_json_create_rect(struct wlr_box *box) {\n\tjson_object *rect = json_object_new_object();\n\n\tjson_object_object_add(rect, \"x\", json_object_new_int(box->x));\n\tjson_object_object_add(rect, \"y\", json_object_new_int(box->y));\n\tjson_object_object_add(rect, \"width\", json_object_new_int(box->width));\n\tjson_object_object_add(rect, \"height\", json_object_new_int(box->height));\n\n\treturn rect;\n}\n\nstatic json_object *ipc_json_create_empty_rect(void) {\n\tstruct wlr_box empty = {0, 0, 0, 0};\n\n\treturn ipc_json_create_rect(&empty);\n}\n\nstatic json_object *ipc_json_create_node(int id, const char* type, char *name,\n\t\tbool focused, json_object *focus, struct wlr_box *box) {\n\tjson_object *object = json_object_new_object();\n\n\tjson_object_object_add(object, \"id\", json_object_new_int(id));\n\tjson_object_object_add(object, \"type\", json_object_new_string(type));\n\tjson_object_object_add(object, \"orientation\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_orientation_description(L_HORIZ)));\n\tjson_object_object_add(object, \"percent\", NULL);\n\tjson_object_object_add(object, \"urgent\", json_object_new_boolean(false));\n\tjson_object_object_add(object, \"marks\", json_object_new_array());\n\tjson_object_object_add(object, \"focused\", json_object_new_boolean(focused));\n\tjson_object_object_add(object, \"layout\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_layout_description(L_HORIZ)));\n\n\t// set default values to be compatible with i3\n\tjson_object_object_add(object, \"border\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_border_description(B_NONE)));\n\tjson_object_object_add(object, \"current_border_width\",\n\t\t\tjson_object_new_int(0));\n\tjson_object_object_add(object, \"rect\", ipc_json_create_rect(box));\n\tjson_object_object_add(object, \"deco_rect\", ipc_json_create_empty_rect());\n\tjson_object_object_add(object, \"window_rect\", ipc_json_create_empty_rect());\n\tjson_object_object_add(object, \"geometry\", ipc_json_create_empty_rect());\n\tjson_object_object_add(object, \"name\",\n\t\t\tname ? json_object_new_string(name) : NULL);\n\tjson_object_object_add(object, \"window\", NULL);\n\tjson_object_object_add(object, \"nodes\", json_object_new_array());\n\tjson_object_object_add(object, \"floating_nodes\", json_object_new_array());\n\tjson_object_object_add(object, \"focus\", focus);\n\tjson_object_object_add(object, \"fullscreen_mode\", json_object_new_int(0));\n\tjson_object_object_add(object, \"sticky\", json_object_new_boolean(false));\n\tjson_object_object_add(object, \"floating\", NULL);\n\tjson_object_object_add(object, \"scratchpad_state\", NULL);\n\n\treturn object;\n}\n\nstatic void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {\n\tjson_object_object_add(object, \"primary\", json_object_new_boolean(false));\n\tjson_object_object_add(object, \"make\",\n\t\t\tjson_object_new_string(wlr_output->make ? wlr_output->make : \"Unknown\"));\n\tjson_object_object_add(object, \"model\",\n\t\t\tjson_object_new_string(wlr_output->model ? wlr_output->model : \"Unknown\"));\n\tjson_object_object_add(object, \"serial\",\n\t\t\tjson_object_new_string(wlr_output->serial ? wlr_output->serial : \"Unknown\"));\n\n\tjson_object *modes_array = json_object_new_array();\n\tstruct wlr_output_mode *mode;\n\twl_list_for_each(mode, &wlr_output->modes, link) {\n\t\tjson_object *mode_object = json_object_new_object();\n\t\tjson_object_object_add(mode_object, \"width\",\n\t\t\tjson_object_new_int(mode->width));\n\t\tjson_object_object_add(mode_object, \"height\",\n\t\t\tjson_object_new_int(mode->height));\n\t\tjson_object_object_add(mode_object, \"refresh\",\n\t\t\tjson_object_new_int(mode->refresh));\n\t\tjson_object_array_add(modes_array, mode_object);\n\t}\n\tjson_object_object_add(object, \"modes\", modes_array);\n\n\tjson_object *features_object = json_object_new_object();\n\tjson_object_object_add(features_object, \"adaptive_sync\",\n\t\tjson_object_new_boolean(wlr_output->adaptive_sync_supported ||\n\t\t\twlr_output->adaptive_sync_status ==  WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED));\n\tjson_object_object_add(features_object, \"hdr\",\n\t\tjson_object_new_boolean(output_supports_hdr(wlr_output, NULL)));\n\tjson_object_object_add(object, \"features\", features_object);\n}\n\nstatic void ipc_json_describe_output(struct sway_output *output,\n\t\tjson_object *object) {\n\tipc_json_describe_wlr_output(output->wlr_output, object);\n}\n\nstatic void ipc_json_describe_enabled_output(struct sway_output *output,\n\t\tjson_object *object) {\n\tipc_json_describe_output(output, object);\n\n\tstruct wlr_output *wlr_output = output->wlr_output;\n\tjson_object_object_add(object, \"non_desktop\", json_object_new_boolean(false));\n\tjson_object_object_add(object, \"active\", json_object_new_boolean(true));\n\tjson_object_object_add(object, \"dpms\",\n\t\t\tjson_object_new_boolean(wlr_output->enabled));\n\tjson_object_object_add(object, \"power\",\n\t\t\tjson_object_new_boolean(wlr_output->enabled));\n\tjson_object_object_add(object, \"layout\", json_object_new_string(\"output\"));\n\tjson_object_object_add(object, \"orientation\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_orientation_description(L_NONE)));\n\tjson_object_object_add(object, \"scale\",\n\t\t\tjson_object_new_double(wlr_output->scale));\n\tjson_object_object_add(object, \"scale_filter\",\n\t\tjson_object_new_string(\n\t\t\tsway_output_scale_filter_to_string(output->scale_filter)));\n\tjson_object_object_add(object, \"transform\",\n\t\tjson_object_new_string(\n\t\t\tipc_json_output_transform_description(wlr_output->transform)));\n\tconst char *adaptive_sync_status =\n\t\tipc_json_output_adaptive_sync_status_description(\n\t\t\twlr_output->adaptive_sync_status);\n\tjson_object_object_add(object, \"adaptive_sync_status\",\n\t\tjson_object_new_string(adaptive_sync_status));\n\n\tstruct sway_workspace *ws = output_get_active_workspace(output);\n\tif (!sway_assert(ws, \"Expected output to have a workspace\")) {\n\t\treturn;\n\t}\n\tjson_object_object_add(object, \"current_workspace\",\n\t\t\tjson_object_new_string(ws->name));\n\n\tjson_object *modes_array = json_object_new_array();\n\tstruct wlr_output_mode *mode;\n\twl_list_for_each(mode, &wlr_output->modes, link) {\n\t\tjson_object *mode_object =\n\t\t\tipc_json_output_mode_description(mode);\n\t\tjson_object_array_add(modes_array, mode_object);\n\t}\n\n\tjson_object_object_add(object, \"modes\", modes_array);\n\n\tjson_object *current_mode_object;\n\tif (wlr_output->current_mode != NULL) {\n\t\tcurrent_mode_object =\n\t\t\tipc_json_output_mode_description(wlr_output->current_mode);\n\t} else {\n\t\tcurrent_mode_object = json_object_new_object();\n\t\tjson_object_object_add(current_mode_object, \"width\",\n\t\t\tjson_object_new_int(wlr_output->width));\n\t\tjson_object_object_add(current_mode_object, \"height\",\n\t\t\tjson_object_new_int(wlr_output->height));\n\t\tjson_object_object_add(current_mode_object, \"refresh\",\n\t\t\tjson_object_new_int(wlr_output->refresh));\n\t}\n\tjson_object_object_add(object, \"current_mode\", current_mode_object);\n\n\tstruct sway_node *parent = node_get_parent(&output->node);\n\tstruct wlr_box parent_box = {0, 0, 0, 0};\n\n\tif (parent != NULL) {\n\t\tnode_get_box(parent, &parent_box);\n\t}\n\n\tif (parent_box.width != 0 && parent_box.height != 0) {\n\t\tdouble percent = ((double)output->width / parent_box.width)\n\t\t\t\t* ((double)output->height / parent_box.height);\n\t\tjson_object_object_add(object, \"percent\", json_object_new_double(percent));\n\t}\n\n\tjson_object_object_add(object, \"max_render_time\", json_object_new_int(output->max_render_time));\n\tjson_object_object_add(object, \"allow_tearing\", json_object_new_boolean(output->allow_tearing));\n\tjson_object_object_add(object, \"hdr\", json_object_new_boolean(output->hdr));\n}\n\njson_object *ipc_json_describe_disabled_output(struct sway_output *output) {\n\tstruct wlr_output *wlr_output = output->wlr_output;\n\n\tjson_object *object = json_object_new_object();\n\n\tipc_json_describe_output(output, object);\n\n\tjson_object_object_add(object, \"non_desktop\", json_object_new_boolean(false));\n\tjson_object_object_add(object, \"type\", json_object_new_string(\"output\"));\n\tjson_object_object_add(object, \"name\",\n\t\t\tjson_object_new_string(wlr_output->name));\n\tjson_object_object_add(object, \"active\", json_object_new_boolean(false));\n\tjson_object_object_add(object, \"dpms\", json_object_new_boolean(false));\n\tjson_object_object_add(object, \"power\", json_object_new_boolean(false));\n\n\tjson_object_object_add(object, \"current_workspace\", NULL);\n\n\tjson_object *rect_object = json_object_new_object();\n\tjson_object_object_add(rect_object, \"x\", json_object_new_int(0));\n\tjson_object_object_add(rect_object, \"y\", json_object_new_int(0));\n\tjson_object_object_add(rect_object, \"width\", json_object_new_int(0));\n\tjson_object_object_add(rect_object, \"height\", json_object_new_int(0));\n\tjson_object_object_add(object, \"rect\", rect_object);\n\n\tjson_object_object_add(object, \"percent\", NULL);\n\n\treturn object;\n}\n\njson_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) {\n\tstruct wlr_output *wlr_output = output->wlr_output;\n\n\tjson_object *object = json_object_new_object();\n\n\tipc_json_describe_wlr_output(wlr_output, object);\n\n\tjson_object_object_add(object, \"non_desktop\", json_object_new_boolean(true));\n\tjson_object_object_add(object, \"type\", json_object_new_string(\"output\"));\n\tjson_object_object_add(object, \"name\",\n\t\t\t\tjson_object_new_string(wlr_output->name));\n\n\treturn object;\n}\n\nstatic json_object *ipc_json_describe_scratchpad_output(void) {\n\tstruct wlr_box box;\n\troot_get_box(root, &box);\n\n\t// Create focus stack for __i3_scratch workspace\n\tjson_object *workspace_focus = json_object_new_array();\n\tfor (int i = root->scratchpad->length - 1; i >= 0; --i) {\n\t\tstruct sway_container *container = root->scratchpad->items[i];\n\t\tjson_object_array_add(workspace_focus,\n\t\t\t\tjson_object_new_int(container->node.id));\n\t}\n\n\tjson_object *workspace = ipc_json_create_node(i3_scratch_id, \"workspace\",\n\t\t\t\t\"__i3_scratch\", false, workspace_focus, &box);\n\tjson_object_object_add(workspace, \"fullscreen_mode\", json_object_new_int(1));\n\n\t// List all hidden scratchpad containers as floating nodes\n\tjson_object *floating_array = json_object_new_array();\n\tfor (int i = 0; i < root->scratchpad->length; ++i) {\n\t\tstruct sway_container *container = root->scratchpad->items[i];\n\t\tif (container_is_scratchpad_hidden(container)) {\n\t\t\tjson_object_array_add(floating_array,\n\t\t\t\tipc_json_describe_node_recursive(&container->node));\n\t\t}\n\t}\n\tjson_object_object_add(workspace, \"floating_nodes\", floating_array);\n\n\t// Create focus stack for __i3 output\n\tjson_object *output_focus = json_object_new_array();\n\tjson_object_array_add(output_focus, json_object_new_int(i3_scratch_id));\n\n\tjson_object *output = ipc_json_create_node(i3_output_id, \"output\",\n\t\t\t\t\t\"__i3\", false, output_focus, &box);\n\tjson_object_object_add(output, \"layout\",\n\t\t\tjson_object_new_string(\"output\"));\n\n\tjson_object *nodes = json_object_new_array();\n\tjson_object_array_add(nodes, workspace);\n\tjson_object_object_add(output, \"nodes\", nodes);\n\n\treturn output;\n}\n\nstatic void ipc_json_describe_workspace(struct sway_workspace *workspace,\n\t\tjson_object *object) {\n\tint num;\n\tif (isdigit(workspace->name[0])) {\n\t\terrno = 0;\n\t\tchar *endptr = NULL;\n\t\tlong long parsed_num = strtoll(workspace->name, &endptr, 10);\n\t\tif (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == workspace->name) {\n\t\t\tnum = -1;\n\t\t} else {\n\t\t\tnum = (int) parsed_num;\n\t\t}\n\t} else {\n\t\tnum = -1;\n\t}\n\tjson_object_object_add(object, \"num\", json_object_new_int(num));\n\tjson_object_object_add(object, \"fullscreen_mode\", json_object_new_int(1));\n\tjson_object_object_add(object, \"output\", workspace->output ?\n\t\t\tjson_object_new_string(workspace->output->wlr_output->name) : NULL);\n\tjson_object_object_add(object, \"urgent\",\n\t\t\tjson_object_new_boolean(workspace->urgent));\n\tjson_object_object_add(object, \"representation\", workspace->representation ?\n\t\t\tjson_object_new_string(workspace->representation) : NULL);\n\n\tjson_object_object_add(object, \"layout\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_layout_description(workspace->layout)));\n\tjson_object_object_add(object, \"orientation\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_orientation_description(workspace->layout)));\n\n\t// Floating\n\tjson_object *floating_array = json_object_new_array();\n\tfor (int i = 0; i < workspace->floating->length; ++i) {\n\t\tstruct sway_container *floater = workspace->floating->items[i];\n\t\tjson_object_array_add(floating_array,\n\t\t\t\tipc_json_describe_node_recursive(&floater->node));\n\t}\n\tjson_object_object_add(object, \"floating_nodes\", floating_array);\n}\n\nstatic void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {\n\tenum sway_container_layout parent_layout = container_parent_layout(c);\n\tlist_t *siblings = container_get_siblings(c);\n\tbool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED)\n\t\t&& ((siblings && siblings->length > 1) || !config->hide_lone_tab);\n\tif (((!tab_or_stack || container_is_floating(c)) &&\n\t\t\t\tc->current.border != B_NORMAL) ||\n\t\t\tc->pending.fullscreen_mode != FULLSCREEN_NONE ||\n\t\t\tc->pending.workspace == NULL) {\n\t\tdeco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0;\n\t\treturn;\n\t}\n\n\tif (c->pending.parent) {\n\t\tdeco_rect->x = c->pending.x - c->pending.parent->pending.x;\n\t\tdeco_rect->y = c->pending.y - c->pending.parent->pending.y;\n\t} else {\n\t\tdeco_rect->x = c->pending.x - c->pending.workspace->x;\n\t\tdeco_rect->y = c->pending.y - c->pending.workspace->y;\n\t}\n\tdeco_rect->width = c->pending.width;\n\tdeco_rect->height = container_titlebar_height();\n\n\tif (!container_is_floating(c)) {\n\t\tif (parent_layout == L_TABBED) {\n\t\t\tdeco_rect->width = c->pending.parent\n\t\t\t\t? c->pending.parent->pending.width / c->pending.parent->pending.children->length\n\t\t\t\t: c->pending.workspace->width / c->pending.workspace->tiling->length;\n\t\t\tdeco_rect->x += deco_rect->width * container_sibling_index(c);\n\t\t} else if (parent_layout == L_STACKED) {\n\t\t\tif (!c->view) {\n\t\t\t\tsize_t siblings = container_get_siblings(c)->length;\n\t\t\t\tdeco_rect->y -= deco_rect->height * siblings;\n\t\t\t}\n\t\t\tdeco_rect->y += deco_rect->height * container_sibling_index(c);\n\t\t}\n\t}\n}\n\nstatic void ipc_json_describe_view(struct sway_container *c, json_object *object) {\n\tjson_object_object_add(object, \"pid\", json_object_new_int(c->view->pid));\n\n\tconst char *app_id = view_get_app_id(c->view);\n\tjson_object_object_add(object, \"app_id\",\n\t\t\tapp_id ? json_object_new_string(app_id) : NULL);\n\n\tjson_object_object_add(object, \"foreign_toplevel_identifier\",\n\t\tc->view->ext_foreign_toplevel ?\n\t\t\tjson_object_new_string(c->view->ext_foreign_toplevel->identifier) : NULL);\n\n\tbool visible = view_is_visible(c->view);\n\tjson_object_object_add(object, \"visible\", json_object_new_boolean(visible));\n\n\tbool has_titlebar = c->title_bar.tree->node.enabled;\n\tstruct wlr_box window_box = {\n\t\tc->pending.content_x - c->pending.x,\n\t\thas_titlebar ? 0 : c->pending.content_y - c->pending.y,\n\t\tc->pending.content_width,\n\t\tc->pending.content_height\n\t};\n\n\tjson_object_object_add(object, \"window_rect\", ipc_json_create_rect(&window_box));\n\n\tstruct wlr_box geometry = {0, 0, c->view->natural_width, c->view->natural_height};\n\tjson_object_object_add(object, \"geometry\", ipc_json_create_rect(&geometry));\n\n\tjson_object_object_add(object, \"max_render_time\", json_object_new_int(c->view->max_render_time));\n\n\tjson_object_object_add(object, \"allow_tearing\", json_object_new_boolean(view_can_tear(c->view)));\n\n\tjson_object_object_add(object, \"shell\", json_object_new_string(view_get_shell(c->view)));\n\n\tjson_object_object_add(object, \"inhibit_idle\",\n\t\tjson_object_new_boolean(view_inhibit_idle(c->view)));\n\n\tconst char *sandbox_engine = view_get_sandbox_engine(c->view);\n\tjson_object_object_add(object, \"sandbox_engine\",\n\t\t\tsandbox_engine ? json_object_new_string(sandbox_engine) : NULL);\n\n\tconst char *sandbox_app_id = view_get_sandbox_app_id(c->view);\n\tjson_object_object_add(object, \"sandbox_app_id\",\n\t\t\tsandbox_app_id ? json_object_new_string(sandbox_app_id) : NULL);\n\n\tconst char *sandbox_instance_id = view_get_sandbox_instance_id(c->view);\n\tjson_object_object_add(object, \"sandbox_instance_id\",\n\t\t\tsandbox_instance_id ? json_object_new_string(sandbox_instance_id) : NULL);\n\n\tconst char *tag = view_get_tag(c->view);\n\tjson_object_object_add(object, \"tag\", tag ? json_object_new_string(tag) : NULL);\n\n\tjson_object *idle_inhibitors = json_object_new_object();\n\n\tstruct sway_idle_inhibitor_v1 *user_inhibitor =\n\t\tsway_idle_inhibit_v1_user_inhibitor_for_view(c->view);\n\n\tif (user_inhibitor) {\n\t\tjson_object_object_add(idle_inhibitors, \"user\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_user_idle_inhibitor_description(user_inhibitor->mode)));\n\t} else {\n\t\tjson_object_object_add(idle_inhibitors, \"user\",\n\t\t\tjson_object_new_string(\"none\"));\n\t}\n\n\tstruct sway_idle_inhibitor_v1 *application_inhibitor =\n\t\tsway_idle_inhibit_v1_application_inhibitor_for_view(c->view);\n\n\tif (application_inhibitor) {\n\t\tjson_object_object_add(idle_inhibitors, \"application\",\n\t\t\tjson_object_new_string(\"enabled\"));\n\t} else {\n\t\tjson_object_object_add(idle_inhibitors, \"application\",\n\t\t\tjson_object_new_string(\"none\"));\n\t}\n\n\tjson_object_object_add(object, \"idle_inhibitors\", idle_inhibitors);\n\n\tenum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE;\n\tif (c->view->surface != NULL) {\n\t\tcontent_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1,\n\t\t\tc->view->surface);\n\t}\n\tif (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) {\n\t\tjson_object_object_add(object, \"content_type\",\n\t\t\tjson_object_new_string(ipc_json_content_type_description(content_type)));\n\t}\n\n#if WLR_HAS_XWAYLAND\n\tif (c->view->type == SWAY_VIEW_XWAYLAND) {\n\t\tjson_object_object_add(object, \"window\",\n\t\t\t\tjson_object_new_int(view_get_x11_window_id(c->view)));\n\n\t\tjson_object *window_props = json_object_new_object();\n\n\t\tconst char *class = view_get_class(c->view);\n\t\tif (class) {\n\t\t\tjson_object_object_add(window_props, \"class\", json_object_new_string(class));\n\t\t}\n\t\tconst char *instance = view_get_instance(c->view);\n\t\tif (instance) {\n\t\t\tjson_object_object_add(window_props, \"instance\", json_object_new_string(instance));\n\t\t}\n\t\tif (c->title) {\n\t\t\tjson_object_object_add(window_props, \"title\", json_object_new_string(c->title));\n\t\t}\n\n\t\t// the transient_for key is always present in i3's output\n\t\tuint32_t parent_id = view_get_x11_parent_id(c->view);\n\t\tjson_object_object_add(window_props, \"transient_for\",\n\t\t\t\tparent_id ? json_object_new_int(parent_id) : NULL);\n\n\t\tconst char *role = view_get_window_role(c->view);\n\t\tif (role) {\n\t\t\tjson_object_object_add(window_props, \"window_role\", json_object_new_string(role));\n\t\t}\n\n\t\tuint32_t window_type = view_get_window_type(c->view);\n\t\tif (window_type) {\n\t\t\tjson_object_object_add(window_props, \"window_type\",\n\t\t\t\tjson_object_new_string(\n\t\t\t\t\tipc_json_xwindow_type_description(c->view)));\n\t\t}\n\n\t\tjson_object_object_add(object, \"window_properties\", window_props);\n\t}\n#endif\n}\n\nstatic void ipc_json_describe_container(struct sway_container *c, json_object *object) {\n\tjson_object_object_add(object, \"name\",\n\t\t\tc->title ? json_object_new_string(c->title) : NULL);\n\tbool floating = container_is_floating(c);\n\tif (floating) {\n\t\tjson_object_object_add(object, \"type\",\n\t\t\t\tjson_object_new_string(\"floating_con\"));\n\t}\n\n\tjson_object_object_add(object, \"layout\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_layout_description(c->pending.layout)));\n\n\tjson_object_object_add(object, \"orientation\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_orientation_description(c->pending.layout)));\n\n\tbool urgent = c->view ?\n\t\tview_is_urgent(c->view) : container_has_urgent_child(c);\n\tjson_object_object_add(object, \"urgent\", json_object_new_boolean(urgent));\n\tjson_object_object_add(object, \"sticky\", json_object_new_boolean(c->is_sticky));\n\n\t// sway doesn't track the floating reason, so we can't use \"auto_on\" or \"user_off\"\n\tjson_object_object_add(object, \"floating\",\n\t\t\tjson_object_new_string(floating ? \"user_on\" : \"auto_off\"));\n\n\tjson_object_object_add(object, \"fullscreen_mode\",\n\t\t\tjson_object_new_int(c->pending.fullscreen_mode));\n\n\t// sway doesn't track if window was resized in scratchpad, so we can't use \"changed\"\n\tjson_object_object_add(object, \"scratchpad_state\",\n\t\t\tjson_object_new_string(!c->scratchpad ? \"none\" : \"fresh\"));\n\n\tstruct sway_node *parent = node_get_parent(&c->node);\n\tstruct wlr_box parent_box = {0, 0, 0, 0};\n\n\tif (parent != NULL) {\n\t\tnode_get_box(parent, &parent_box);\n\t}\n\n\tif (parent_box.width != 0 && parent_box.height != 0) {\n\t\tdouble percent = ((double)c->pending.width / parent_box.width)\n\t\t\t\t* ((double)c->pending.height / parent_box.height);\n\t\tjson_object_object_add(object, \"percent\", json_object_new_double(percent));\n\t}\n\n\tjson_object_object_add(object, \"border\",\n\t\t\tjson_object_new_string(\n\t\t\t\tipc_json_border_description(c->current.border)));\n\tjson_object_object_add(object, \"current_border_width\",\n\t\t\tjson_object_new_int(c->current.border_thickness));\n\tjson_object_object_add(object, \"floating_nodes\", json_object_new_array());\n\n\tstruct wlr_box deco_box = {0, 0, 0, 0};\n\tget_deco_rect(c, &deco_box);\n\tjson_object_object_add(object, \"deco_rect\", ipc_json_create_rect(&deco_box));\n\n\tjson_object *marks = json_object_new_array();\n\tlist_t *con_marks = c->marks;\n\tfor (int i = 0; i < con_marks->length; ++i) {\n\t\tjson_object_array_add(marks, json_object_new_string(con_marks->items[i]));\n\t}\n\n\tjson_object_object_add(object, \"marks\", marks);\n\n\tif (c->view) {\n\t\tipc_json_describe_view(c, object);\n\t}\n}\n\nstruct focus_inactive_data {\n\tstruct sway_node *node;\n\tjson_object *object;\n};\n\nstatic void focus_inactive_children_iterator(struct sway_node *node,\n\t\tvoid *_data) {\n\tstruct focus_inactive_data *data = _data;\n\tjson_object *focus = data->object;\n\tif (data->node == &root->node) {\n\t\tstruct sway_output *output = node_get_output(node);\n\t\tif (output == NULL) {\n\t\t\treturn;\n\t\t}\n\t\tsize_t id = output->node.id;\n\t\tint len = json_object_array_length(focus);\n\t\tfor (int i = 0; i < len; ++i) {\n\t\t\tif ((size_t) json_object_get_int(json_object_array_get_idx(focus, i)) == id) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tnode = &output->node;\n\t} else if (node_get_parent(node) != data->node) {\n\t\treturn;\n\t}\n\tjson_object_array_add(focus, json_object_new_int(node->id));\n}\n\njson_object *ipc_json_describe_node(struct sway_node *node) {\n\tstruct sway_seat *seat = input_manager_get_default_seat();\n\tbool focused = seat_get_focus(seat) == node;\n\tchar *name = node_get_name(node);\n\n\tstruct wlr_box box;\n\tnode_get_box(node, &box);\n\tif (node->type == N_CONTAINER) {\n\t\tstruct wlr_box deco_rect = {0, 0, 0, 0};\n\t\tget_deco_rect(node->sway_container, &deco_rect);\n\t\tsize_t count = 1;\n\t\tif (container_parent_layout(node->sway_container) == L_STACKED) {\n\t\t\tcount = container_get_siblings(node->sway_container)->length;\n\t\t}\n\t\tbox.y += deco_rect.height * count;\n\t\tbox.height -= deco_rect.height * count;\n\t}\n\n\tjson_object *focus = json_object_new_array();\n\tstruct focus_inactive_data data = {\n\t\t.node = node,\n\t\t.object = focus,\n\t};\n\tseat_for_each_node(seat, focus_inactive_children_iterator, &data);\n\n\tjson_object *object = ipc_json_create_node((int)node->id,\n\t\t\t\tipc_json_node_type_description(node->type), name, focused, focus, &box);\n\n\tswitch (node->type) {\n\tcase N_ROOT:\n\t\tbreak;\n\tcase N_OUTPUT:\n\t\tipc_json_describe_enabled_output(node->sway_output, object);\n\t\tbreak;\n\tcase N_CONTAINER:\n\t\tipc_json_describe_container(node->sway_container, object);\n\t\tbreak;\n\tcase N_WORKSPACE:\n\t\tipc_json_describe_workspace(node->sway_workspace, object);\n\t\tbreak;\n\t}\n\n\treturn object;\n}\n\njson_object *ipc_json_describe_node_recursive(struct sway_node *node) {\n\tjson_object *object = ipc_json_describe_node(node);\n\tint i;\n\n\tjson_object *children = json_object_new_array();\n\tswitch (node->type) {\n\tcase N_ROOT:\n\t\tjson_object_array_add(children,\n\t\t\t\tipc_json_describe_scratchpad_output());\n\t\tfor (i = 0; i < root->outputs->length; ++i) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tjson_object_array_add(children,\n\t\t\t\t\tipc_json_describe_node_recursive(&output->node));\n\t\t}\n\t\tbreak;\n\tcase N_OUTPUT:\n\t\tfor (i = 0; i < node->sway_output->workspaces->length; ++i) {\n\t\t\tstruct sway_workspace *ws = node->sway_output->workspaces->items[i];\n\t\t\tjson_object_array_add(children,\n\t\t\t\t\tipc_json_describe_node_recursive(&ws->node));\n\t\t}\n\t\tbreak;\n\tcase N_WORKSPACE:\n\t\tfor (i = 0; i < node->sway_workspace->tiling->length; ++i) {\n\t\t\tstruct sway_container *con = node->sway_workspace->tiling->items[i];\n\t\t\tjson_object_array_add(children,\n\t\t\t\t\tipc_json_describe_node_recursive(&con->node));\n\t\t}\n\t\tbreak;\n\tcase N_CONTAINER:\n\t\tif (node->sway_container->pending.children) {\n\t\t\tfor (i = 0; i < node->sway_container->pending.children->length; ++i) {\n\t\t\t\tstruct sway_container *child =\n\t\t\t\t\tnode->sway_container->pending.children->items[i];\n\t\t\t\tjson_object_array_add(children,\n\t\t\t\t\t\tipc_json_describe_node_recursive(&child->node));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\tjson_object_object_add(object, \"nodes\", children);\n\n\treturn object;\n}\n\n#if WLR_HAS_LIBINPUT_BACKEND\nstatic json_object *describe_libinput_device(struct libinput_device *device) {\n\tjson_object *object = json_object_new_object();\n\n\tconst char *events = \"unknown\";\n\tswitch (libinput_device_config_send_events_get_mode(device)) {\n\tcase LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:\n\t\tevents = \"enabled\";\n\t\tbreak;\n\tcase LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:\n\t\tevents = \"disabled_on_external_mouse\";\n\t\tbreak;\n\tcase LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:\n\t\tevents = \"disabled\";\n\t\tbreak;\n\t}\n\tjson_object_object_add(object, \"send_events\",\n\t\t\tjson_object_new_string(events));\n\n\tif (libinput_device_config_tap_get_finger_count(device) > 0) {\n\t\tconst char *tap = \"unknown\";\n\t\tswitch (libinput_device_config_tap_get_enabled(device)) {\n\t\tcase LIBINPUT_CONFIG_TAP_ENABLED:\n\t\t\ttap = \"enabled\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_TAP_DISABLED:\n\t\t\ttap = \"disabled\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"tap\", json_object_new_string(tap));\n\n\t\tconst char *button_map = \"unknown\";\n\t\tswitch (libinput_device_config_tap_get_button_map(device)) {\n\t\tcase LIBINPUT_CONFIG_TAP_MAP_LRM:\n\t\t\tbutton_map = \"lrm\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_TAP_MAP_LMR:\n\t\t\tbutton_map = \"lmr\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"tap_button_map\",\n\t\t\t\tjson_object_new_string(button_map));\n\n\t\tconst char* drag = \"unknown\";\n\t\tswitch (libinput_device_config_tap_get_drag_enabled(device)) {\n\t\tcase LIBINPUT_CONFIG_DRAG_ENABLED:\n\t\t\tdrag = \"enabled\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_DRAG_DISABLED:\n\t\t\tdrag = \"disabled\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"tap_drag\",\n\t\t\t\tjson_object_new_string(drag));\n\n\t\tconst char *drag_lock = \"unknown\";\n\t\tswitch (libinput_device_config_tap_get_drag_lock_enabled(device)) {\n\t\tcase LIBINPUT_CONFIG_DRAG_LOCK_ENABLED:\n\t\t\tdrag_lock = \"enabled\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_DRAG_LOCK_DISABLED:\n\t\t\tdrag_lock = \"disabled\";\n\t\t\tbreak;\n#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY\n\t\tcase LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY:\n\t\t\tdrag_lock = \"enabled_sticky\";\n\t\t\tbreak;\n#endif\n\t\t}\n\t\tjson_object_object_add(object, \"tap_drag_lock\",\n\t\t\t\tjson_object_new_string(drag_lock));\n\t}\n\n\tif (libinput_device_config_accel_is_available(device)) {\n\t\tdouble accel = libinput_device_config_accel_get_speed(device);\n\t\tjson_object_object_add(object, \"accel_speed\",\n\t\t\t\tjson_object_new_double(accel));\n\n\t\tconst char *accel_profile = \"unknown\";\n\t\tswitch (libinput_device_config_accel_get_profile(device)) {\n\t\tcase LIBINPUT_CONFIG_ACCEL_PROFILE_NONE:\n\t\t\taccel_profile = \"none\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:\n\t\t\taccel_profile = \"flat\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:\n\t\t\taccel_profile = \"adaptive\";\n\t\t\tbreak;\n#if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM\n\t\tcase LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:\n\t\t\taccel_profile = \"custom\";\n\t\t\tbreak;\n#endif\n\t\t}\n\t\tjson_object_object_add(object, \"accel_profile\",\n\t\t\t\tjson_object_new_string(accel_profile));\n\t}\n\n\tif (libinput_device_config_scroll_has_natural_scroll(device)) {\n\t\tconst char *natural_scroll = \"disabled\";\n\t\tif (libinput_device_config_scroll_get_natural_scroll_enabled(device)) {\n\t\t\tnatural_scroll = \"enabled\";\n\t\t}\n\t\tjson_object_object_add(object, \"natural_scroll\",\n\t\t\t\tjson_object_new_string(natural_scroll));\n\t}\n\n\tif (libinput_device_config_left_handed_is_available(device)) {\n\t\tconst char *left_handed = \"disabled\";\n\t\tif (libinput_device_config_left_handed_get(device) != 0) {\n\t\t\tleft_handed = \"enabled\";\n\t\t}\n\t\tjson_object_object_add(object, \"left_handed\",\n\t\t\t\tjson_object_new_string(left_handed));\n\t}\n\n\tuint32_t click_methods = libinput_device_config_click_get_methods(device);\n\tif ((click_methods & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) != 0) {\n\t\tconst char *click_method = \"unknown\";\n\t\tswitch (libinput_device_config_click_get_method(device)) {\n\t\tcase LIBINPUT_CONFIG_CLICK_METHOD_NONE:\n\t\t\tclick_method = \"none\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS:\n\t\t\tclick_method = \"button_areas\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER:\n\t\t\tclick_method = \"clickfinger\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"click_method\",\n\t\t\t\tjson_object_new_string(click_method));\n\n\t\tconst char *button_map = \"unknown\";\n\t\tswitch (libinput_device_config_click_get_clickfinger_button_map(device)) {\n\t\tcase LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM:\n\t\t\tbutton_map = \"lrm\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR:\n\t\t\tbutton_map = \"lmr\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"clickfinger_button_map\",\n\t\t\t\tjson_object_new_string(button_map));\n\t}\n\n\tif (libinput_device_config_middle_emulation_is_available(device)) {\n\t\tconst char *middle_emulation = \"unknown\";\n\t\tswitch (libinput_device_config_middle_emulation_get_enabled(device)) {\n\t\tcase LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:\n\t\t\tmiddle_emulation = \"enabled\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:\n\t\t\tmiddle_emulation = \"disabled\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"middle_emulation\",\n\t\t\t\tjson_object_new_string(middle_emulation));\n\t}\n\n\tuint32_t scroll_methods = libinput_device_config_scroll_get_methods(device);\n\tif ((scroll_methods & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) != 0) {\n\t\tconst char *scroll_method = \"unknown\";\n\t\tswitch (libinput_device_config_scroll_get_method(device)) {\n\t\tcase LIBINPUT_CONFIG_SCROLL_NO_SCROLL:\n\t\t\tscroll_method = \"none\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_SCROLL_2FG:\n\t\t\tscroll_method = \"two_finger\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_SCROLL_EDGE:\n\t\t\tscroll_method = \"edge\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:\n\t\t\tscroll_method = \"on_button_down\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"scroll_method\",\n\t\t\t\tjson_object_new_string(scroll_method));\n\n\t\tif ((scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) != 0) {\n\t\t\tuint32_t button = libinput_device_config_scroll_get_button(device);\n\t\t\tjson_object_object_add(object, \"scroll_button\",\n\t\t\t\t\tjson_object_new_int(button));\n\t\t\tconst char *lock = \"unknown\";\n\t\t\tswitch (libinput_device_config_scroll_get_button_lock(device)) {\n\t\t\tcase LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED:\n\t\t\t\tlock = \"enabled\";\n\t\t\t\tbreak;\n\t\t\tcase LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED:\n\t\t\t\tlock = \"disabled\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tjson_object_object_add(object, \"scroll_button_lock\",\n\t\t\t\t\tjson_object_new_string(lock));\n\t\t}\n\t}\n\n\tif (libinput_device_config_dwt_is_available(device)) {\n\t\tconst char *dwt = \"unknown\";\n\t\tswitch (libinput_device_config_dwt_get_enabled(device)) {\n\t\tcase LIBINPUT_CONFIG_DWT_ENABLED:\n\t\t\tdwt = \"enabled\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_DWT_DISABLED:\n\t\t\tdwt = \"disabled\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"dwt\", json_object_new_string(dwt));\n\t}\n\n\tif (libinput_device_config_dwtp_is_available(device)) {\n\t\tconst char *dwtp = \"unknown\";\n\t\tswitch (libinput_device_config_dwtp_get_enabled(device)) {\n\t\tcase LIBINPUT_CONFIG_DWTP_ENABLED:\n\t\t\tdwtp = \"enabled\";\n\t\t\tbreak;\n\t\tcase LIBINPUT_CONFIG_DWTP_DISABLED:\n\t\t\tdwtp = \"disabled\";\n\t\t\tbreak;\n\t\t}\n\t\tjson_object_object_add(object, \"dwtp\", json_object_new_string(dwtp));\n\t}\n\n\tif (libinput_device_config_calibration_has_matrix(device)) {\n\t\tfloat matrix[6];\n\t\tlibinput_device_config_calibration_get_matrix(device, matrix);\n\t\tstruct json_object* array = json_object_new_array();\n\t\tstruct json_object* x;\n\t\tfor (int i = 0; i < 6; i++) {\n\t\t\tx = json_object_new_double(matrix[i]);\n\t\t\tjson_object_array_add(array, x);\n\t\t}\n\t\tjson_object_object_add(object, \"calibration_matrix\", array);\n\t}\n\n\treturn object;\n}\n#endif\n\njson_object *ipc_json_describe_input(struct sway_input_device *device) {\n\tif (!(sway_assert(device, \"Device must not be null\"))) {\n\t\treturn NULL;\n\t}\n\n\tjson_object *object = json_object_new_object();\n\n\tjson_object_object_add(object, \"identifier\",\n\t\tjson_object_new_string(device->identifier));\n\tjson_object_object_add(object, \"name\",\n\t\tjson_object_new_string(device->wlr_device->name));\n\tjson_object_object_add(object, \"type\",\n\t\tjson_object_new_string(\n\t\t\tinput_device_get_type(device)));\n\n\tif (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {\n\t\tstruct wlr_keyboard *keyboard =\n\t\t\twlr_keyboard_from_input_device(device->wlr_device);\n\t\tstruct xkb_keymap *keymap = keyboard->keymap;\n\t\tstruct xkb_state *state = keyboard->xkb_state;\n\n\t\tjson_object_object_add(object, \"repeat_delay\",\n\t\t\tjson_object_new_int(keyboard->repeat_info.delay));\n\t\tjson_object_object_add(object, \"repeat_rate\",\n\t\t\tjson_object_new_int(keyboard->repeat_info.rate));\n\n\t\tjson_object *layouts_arr = json_object_new_array();\n\t\tjson_object_object_add(object, \"xkb_layout_names\", layouts_arr);\n\n\t\txkb_layout_index_t num_layouts =\n\t\t\tkeymap ? xkb_keymap_num_layouts(keymap) : 0;\n\t\t// Virtual keyboards might have null keymap\n\t\txkb_layout_index_t layout_idx;\n\t\tfor (layout_idx = 0; layout_idx < num_layouts; layout_idx++) {\n\t\t\tconst char *layout = xkb_keymap_layout_get_name(keymap, layout_idx);\n\t\t\tjson_object_array_add(layouts_arr,\n\t\t\t\tlayout ? json_object_new_string(layout) : NULL);\n\n\t\t\tbool is_active = xkb_state_layout_index_is_active(state,\n\t\t\t\tlayout_idx, XKB_STATE_LAYOUT_EFFECTIVE);\n\t\t\tif (is_active) {\n\t\t\t\tjson_object_object_add(object, \"xkb_active_layout_index\",\n\t\t\t\t\tjson_object_new_int(layout_idx));\n\t\t\t\tjson_object_object_add(object, \"xkb_active_layout_name\",\n\t\t\t\t\tlayout ? json_object_new_string(layout) : NULL);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {\n\t\tstruct input_config *ic = input_device_get_config(device);\n\t\tfloat scroll_factor = 1.0f;\n\t\tif (ic != NULL && !isnan(ic->scroll_factor) &&\n\t\t\t\tic->scroll_factor != FLT_MIN) {\n\t\t\tscroll_factor = ic->scroll_factor;\n\t\t}\n\t\tjson_object_object_add(object, \"scroll_factor\",\n\t\t\t\tjson_object_new_double(scroll_factor));\n\t}\n\n#if WLR_HAS_LIBINPUT_BACKEND\n\tif (wlr_input_device_is_libinput(device->wlr_device)) {\n\t\tstruct libinput_device *libinput_dev;\n\t\tlibinput_dev = wlr_libinput_get_device_handle(device->wlr_device);\n\t\tjson_object_object_add(object, \"libinput\",\n\t\t\t\tdescribe_libinput_device(libinput_dev));\n\t\tjson_object_object_add(object, \"vendor\",\n\t\t\tjson_object_new_int(libinput_device_get_id_vendor(libinput_dev)));\n\t\tjson_object_object_add(object, \"product\",\n\t\t\tjson_object_new_int(libinput_device_get_id_product(libinput_dev)));\n\t}\n#endif\n\n\treturn object;\n}\n\njson_object *ipc_json_describe_seat(struct sway_seat *seat) {\n\tif (!(sway_assert(seat, \"Seat must not be null\"))) {\n\t\treturn NULL;\n\t}\n\n\tjson_object *object = json_object_new_object();\n\tstruct sway_node *focus = seat_get_focus(seat);\n\n\tjson_object_object_add(object, \"name\",\n\t\tjson_object_new_string(seat->wlr_seat->name));\n\tjson_object_object_add(object, \"capabilities\",\n\t\tjson_object_new_int(seat->wlr_seat->capabilities));\n\tjson_object_object_add(object, \"focus\",\n\t\tjson_object_new_int(focus ? focus->id : 0));\n\n\tjson_object *devices = json_object_new_array();\n\tstruct sway_seat_device *device = NULL;\n\twl_list_for_each(device, &seat->devices, link) {\n\t\tjson_object_array_add(devices, ipc_json_describe_input(device->input_device));\n\t}\n\tjson_object_object_add(object, \"devices\", devices);\n\n\treturn object;\n}\n\nstatic uint32_t event_to_x11_button(uint32_t event) {\n\tswitch (event) {\n\tcase BTN_LEFT:\n\t\treturn 1;\n\tcase BTN_MIDDLE:\n\t\treturn 2;\n\tcase BTN_RIGHT:\n\t\treturn 3;\n\tcase SWAY_SCROLL_UP:\n\t\treturn 4;\n\tcase SWAY_SCROLL_DOWN:\n\t\treturn 5;\n\tcase SWAY_SCROLL_LEFT:\n\t\treturn 6;\n\tcase SWAY_SCROLL_RIGHT:\n\t\treturn 7;\n\tcase BTN_SIDE:\n\t\treturn 8;\n\tcase BTN_EXTRA:\n\t\treturn 9;\n\tdefault:\n\t\treturn 0;\n\t}\n}\n\njson_object *ipc_json_describe_bar_config(struct bar_config *bar) {\n\tif (!sway_assert(bar, \"Bar must not be NULL\")) {\n\t\treturn NULL;\n\t}\n\n\tjson_object *json = json_object_new_object();\n\tjson_object_object_add(json, \"id\", json_object_new_string(bar->id));\n\tjson_object_object_add(json, \"mode\", json_object_new_string(bar->mode));\n\tjson_object_object_add(json, \"hidden_state\",\n\t\t\tjson_object_new_string(bar->hidden_state));\n\tjson_object_object_add(json, \"position\",\n\t\t\tjson_object_new_string(bar->position));\n\tjson_object_object_add(json, \"status_command\", bar->status_command ?\n\t\t\tjson_object_new_string(bar->status_command) : NULL);\n\tjson_object_object_add(json, \"font\",\n\t\t\tjson_object_new_string((bar->font) ? bar->font : config->font));\n\n\tjson_object *gaps = json_object_new_object();\n\tjson_object_object_add(gaps, \"top\",\n\t\t\tjson_object_new_int(bar->gaps.top));\n\tjson_object_object_add(gaps, \"right\",\n\t\t\tjson_object_new_int(bar->gaps.right));\n\tjson_object_object_add(gaps, \"bottom\",\n\t\t\tjson_object_new_int(bar->gaps.bottom));\n\tjson_object_object_add(gaps, \"left\",\n\t\t\tjson_object_new_int(bar->gaps.left));\n\tjson_object_object_add(json, \"gaps\", gaps);\n\n\tif (bar->separator_symbol) {\n\t\tjson_object_object_add(json, \"separator_symbol\",\n\t\t\t\tjson_object_new_string(bar->separator_symbol));\n\t}\n\tjson_object_object_add(json, \"bar_height\",\n\t\t\tjson_object_new_int(bar->height));\n\tjson_object_object_add(json, \"status_padding\",\n\t\t\tjson_object_new_int(bar->status_padding));\n\tjson_object_object_add(json, \"status_edge_padding\",\n\t\t\tjson_object_new_int(bar->status_edge_padding));\n\tjson_object_object_add(json, \"wrap_scroll\",\n\t\t\tjson_object_new_boolean(bar->wrap_scroll));\n\tjson_object_object_add(json, \"workspace_buttons\",\n\t\t\tjson_object_new_boolean(bar->workspace_buttons));\n\tjson_object_object_add(json, \"strip_workspace_numbers\",\n\t\t\tjson_object_new_boolean(bar->strip_workspace_numbers));\n\tjson_object_object_add(json, \"strip_workspace_name\",\n\t\t\tjson_object_new_boolean(bar->strip_workspace_name));\n\tjson_object_object_add(json, \"workspace_min_width\",\n\t\t\tjson_object_new_int(bar->workspace_min_width));\n\tjson_object_object_add(json, \"binding_mode_indicator\",\n\t\t\tjson_object_new_boolean(bar->binding_mode_indicator));\n\tjson_object_object_add(json, \"verbose\",\n\t\t\tjson_object_new_boolean(bar->verbose));\n\tjson_object_object_add(json, \"pango_markup\",\n\t\t\tjson_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT\n\t\t\t\t\t\t\t\t\t\t\t? config->pango_markup\n\t\t\t\t\t\t\t\t\t\t\t: bar->pango_markup));\n\n\tjson_object *colors = json_object_new_object();\n\tjson_object_object_add(colors, \"background\",\n\t\t\tjson_object_new_string(bar->colors.background));\n\tjson_object_object_add(colors, \"statusline\",\n\t\t\tjson_object_new_string(bar->colors.statusline));\n\tjson_object_object_add(colors, \"separator\",\n\t\t\tjson_object_new_string(bar->colors.separator));\n\n\tif (bar->colors.focused_background) {\n\t\tjson_object_object_add(colors, \"focused_background\",\n\t\t\t\tjson_object_new_string(bar->colors.focused_background));\n\t} else {\n\t\tjson_object_object_add(colors, \"focused_background\",\n\t\t\t\tjson_object_new_string(bar->colors.background));\n\t}\n\n\tif (bar->colors.focused_statusline) {\n\t\tjson_object_object_add(colors, \"focused_statusline\",\n\t\t\t\tjson_object_new_string(bar->colors.focused_statusline));\n\t} else {\n\t\tjson_object_object_add(colors, \"focused_statusline\",\n\t\t\t\tjson_object_new_string(bar->colors.statusline));\n\t}\n\n\tif (bar->colors.focused_separator) {\n\t\tjson_object_object_add(colors, \"focused_separator\",\n\t\t\t\tjson_object_new_string(bar->colors.focused_separator));\n\t} else {\n\t\tjson_object_object_add(colors, \"focused_separator\",\n\t\t\t\tjson_object_new_string(bar->colors.separator));\n\t}\n\n\tjson_object_object_add(colors, \"focused_workspace_border\",\n\t\t\tjson_object_new_string(bar->colors.focused_workspace_border));\n\tjson_object_object_add(colors, \"focused_workspace_bg\",\n\t\t\tjson_object_new_string(bar->colors.focused_workspace_bg));\n\tjson_object_object_add(colors, \"focused_workspace_text\",\n\t\t\tjson_object_new_string(bar->colors.focused_workspace_text));\n\n\tjson_object_object_add(colors, \"inactive_workspace_border\",\n\t\t\tjson_object_new_string(bar->colors.inactive_workspace_border));\n\tjson_object_object_add(colors, \"inactive_workspace_bg\",\n\t\t\tjson_object_new_string(bar->colors.inactive_workspace_bg));\n\tjson_object_object_add(colors, \"inactive_workspace_text\",\n\t\t\tjson_object_new_string(bar->colors.inactive_workspace_text));\n\n\tjson_object_object_add(colors, \"active_workspace_border\",\n\t\t\tjson_object_new_string(bar->colors.active_workspace_border));\n\tjson_object_object_add(colors, \"active_workspace_bg\",\n\t\t\tjson_object_new_string(bar->colors.active_workspace_bg));\n\tjson_object_object_add(colors, \"active_workspace_text\",\n\t\t\tjson_object_new_string(bar->colors.active_workspace_text));\n\n\tjson_object_object_add(colors, \"urgent_workspace_border\",\n\t\t\tjson_object_new_string(bar->colors.urgent_workspace_border));\n\tjson_object_object_add(colors, \"urgent_workspace_bg\",\n\t\t\tjson_object_new_string(bar->colors.urgent_workspace_bg));\n\tjson_object_object_add(colors, \"urgent_workspace_text\",\n\t\t\tjson_object_new_string(bar->colors.urgent_workspace_text));\n\n\tif (bar->colors.binding_mode_border) {\n\t\tjson_object_object_add(colors, \"binding_mode_border\",\n\t\t\t\tjson_object_new_string(bar->colors.binding_mode_border));\n\t} else {\n\t\tjson_object_object_add(colors, \"binding_mode_border\",\n\t\t\t\tjson_object_new_string(bar->colors.urgent_workspace_border));\n\t}\n\n\tif (bar->colors.binding_mode_bg) {\n\t\tjson_object_object_add(colors, \"binding_mode_bg\",\n\t\t\t\tjson_object_new_string(bar->colors.binding_mode_bg));\n\t} else {\n\t\tjson_object_object_add(colors, \"binding_mode_bg\",\n\t\t\t\tjson_object_new_string(bar->colors.urgent_workspace_bg));\n\t}\n\n\tif (bar->colors.binding_mode_text) {\n\t\tjson_object_object_add(colors, \"binding_mode_text\",\n\t\t\t\tjson_object_new_string(bar->colors.binding_mode_text));\n\t} else {\n\t\tjson_object_object_add(colors, \"binding_mode_text\",\n\t\t\t\tjson_object_new_string(bar->colors.urgent_workspace_text));\n\t}\n\n\tjson_object_object_add(json, \"colors\", colors);\n\n\tif (bar->bindings->length > 0) {\n\t\tjson_object *bindings = json_object_new_array();\n\t\tfor (int i = 0; i < bar->bindings->length; ++i) {\n\t\t\tstruct bar_binding *binding = bar->bindings->items[i];\n\t\t\tjson_object *bind = json_object_new_object();\n\t\t\tjson_object_object_add(bind, \"input_code\",\n\t\t\t\t\tjson_object_new_int(event_to_x11_button(binding->button)));\n\t\t\tjson_object_object_add(bind, \"event_code\",\n\t\t\t\t\tjson_object_new_int(binding->button));\n\t\t\tjson_object_object_add(bind, \"command\",\n\t\t\t\t\tjson_object_new_string(binding->command));\n\t\t\tjson_object_object_add(bind, \"release\",\n\t\t\t\t\tjson_object_new_boolean(binding->release));\n\t\t\tjson_object_array_add(bindings, bind);\n\t\t}\n\t\tjson_object_object_add(json, \"bindings\", bindings);\n\t}\n\n\t// Add outputs if defined\n\tif (bar->outputs && bar->outputs->length > 0) {\n\t\tjson_object *outputs = json_object_new_array();\n\t\tfor (int i = 0; i < bar->outputs->length; ++i) {\n\t\t\tconst char *name = bar->outputs->items[i];\n\t\t\tjson_object_array_add(outputs, json_object_new_string(name));\n\t\t}\n\t\tjson_object_object_add(json, \"outputs\", outputs);\n\t}\n#if HAVE_TRAY\n\t// Add tray outputs if defined\n\tif (bar->tray_outputs && bar->tray_outputs->length > 0) {\n\t\tjson_object *tray_outputs = json_object_new_array();\n\t\tfor (int i = 0; i < bar->tray_outputs->length; ++i) {\n\t\t\tconst char *name = bar->tray_outputs->items[i];\n\t\t\tjson_object_array_add(tray_outputs, json_object_new_string(name));\n\t\t}\n\t\tjson_object_object_add(json, \"tray_outputs\", tray_outputs);\n\t}\n\n\tjson_object *tray_bindings = json_object_new_array();\n\tstruct tray_binding *tray_bind = NULL;\n\twl_list_for_each(tray_bind, &bar->tray_bindings, link) {\n\t\tjson_object *bind = json_object_new_object();\n\t\tjson_object_object_add(bind, \"input_code\",\n\t\t\t\tjson_object_new_int(event_to_x11_button(tray_bind->button)));\n\t\tjson_object_object_add(bind, \"event_code\",\n\t\t\t\tjson_object_new_int(tray_bind->button));\n\t\tjson_object_object_add(bind, \"command\",\n\t\t\t\tjson_object_new_string(tray_bind->command));\n\t\tjson_object_array_add(tray_bindings, bind);\n\t}\n\tif (json_object_array_length(tray_bindings) > 0) {\n\t\tjson_object_object_add(json, \"tray_bindings\", tray_bindings);\n\t} else {\n\t\tjson_object_put(tray_bindings);\n\t}\n\n\tif (bar->icon_theme) {\n\t\tjson_object_object_add(json, \"icon_theme\",\n\t\t\t\tjson_object_new_string(bar->icon_theme));\n\t}\n\n\tjson_object_object_add(json, \"tray_padding\",\n\t\t\tjson_object_new_int(bar->tray_padding));\n#endif\n\treturn json;\n}\n\njson_object *ipc_json_get_binding_mode(void) {\n\tjson_object *current_mode = json_object_new_object();\n\tjson_object_object_add(current_mode, \"name\",\n\t\t\tjson_object_new_string(config->current_mode->name));\n\treturn current_mode;\n}\n"
  },
  {
    "path": "sway/ipc-server.c",
    "content": "// See https://i3wm.org/docs/ipc.html for protocol information\n#include <linux/input-event-codes.h>\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <json.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/ioctl.h>\n#include <sys/un.h>\n#include <unistd.h>\n#include <wayland-server-core.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/ipc-json.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstatic int ipc_socket = -1;\nstatic struct wl_event_source *ipc_event_source =  NULL;\nstatic struct sockaddr_un *ipc_sockaddr = NULL;\nstatic list_t *ipc_client_list = NULL;\nstatic struct wl_listener ipc_display_destroy;\n\nstatic const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};\n\n#define IPC_HEADER_SIZE (sizeof(ipc_magic) + 8)\n\nstruct ipc_client {\n\tstruct wl_event_source *event_source;\n\tstruct wl_event_source *writable_event_source;\n\tstruct sway_server *server;\n\tint fd;\n\tenum ipc_command_type subscribed_events;\n\tsize_t write_buffer_len;\n\tsize_t write_buffer_size;\n\tchar *write_buffer;\n\t// The following are for storing data between event_loop calls\n\tuint32_t pending_length;\n\tenum ipc_command_type pending_type;\n};\n\nint ipc_handle_connection(int fd, uint32_t mask, void *data);\nint ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);\nint ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);\nvoid ipc_client_disconnect(struct ipc_client *client);\nvoid ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length,\n\tenum ipc_command_type payload_type);\nbool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,\n\tconst char *payload, uint32_t payload_length);\n\nstatic void handle_display_destroy(struct wl_listener *listener, void *data) {\n\tif (ipc_event_source) {\n\t\twl_event_source_remove(ipc_event_source);\n\t}\n\tclose(ipc_socket);\n\tunlink(ipc_sockaddr->sun_path);\n\n\twhile (ipc_client_list->length) {\n\t\tipc_client_disconnect(ipc_client_list->items[ipc_client_list->length-1]);\n\t}\n\tlist_free(ipc_client_list);\n\n\tfree(ipc_sockaddr);\n\n\twl_list_remove(&ipc_display_destroy.link);\n}\n\nvoid ipc_init(struct sway_server *server) {\n\tipc_socket = socket(AF_UNIX, SOCK_STREAM, 0);\n\tif (ipc_socket == -1) {\n\t\tsway_abort(\"Unable to create IPC socket\");\n\t}\n\tif (fcntl(ipc_socket, F_SETFD, FD_CLOEXEC) == -1) {\n\t\tsway_abort(\"Unable to set CLOEXEC on IPC socket\");\n\t}\n\tif (fcntl(ipc_socket, F_SETFL, O_NONBLOCK) == -1) {\n\t\tsway_abort(\"Unable to set NONBLOCK on IPC socket\");\n\t}\n\n\tipc_sockaddr = ipc_user_sockaddr();\n\n\t// We want to use socket name set by user, not existing socket from another sway instance.\n\tif (getenv(\"SWAYSOCK\") != NULL && access(getenv(\"SWAYSOCK\"), F_OK) == -1) {\n\t\tstrncpy(ipc_sockaddr->sun_path, getenv(\"SWAYSOCK\"), sizeof(ipc_sockaddr->sun_path) - 1);\n\t\tipc_sockaddr->sun_path[sizeof(ipc_sockaddr->sun_path) - 1] = 0;\n\t}\n\n\tunlink(ipc_sockaddr->sun_path);\n\tif (bind(ipc_socket, (struct sockaddr *)ipc_sockaddr, sizeof(*ipc_sockaddr)) == -1) {\n\t\tsway_abort(\"Unable to bind IPC socket\");\n\t}\n\n\tif (listen(ipc_socket, 3) == -1) {\n\t\tsway_abort(\"Unable to listen on IPC socket\");\n\t}\n\n\t// Set i3 IPC socket path so that i3-msg works out of the box\n\tsetenv(\"I3SOCK\", ipc_sockaddr->sun_path, 1);\n\tsetenv(\"SWAYSOCK\", ipc_sockaddr->sun_path, 1);\n\n\tipc_client_list = create_list();\n\n\tipc_display_destroy.notify = handle_display_destroy;\n\twl_display_add_destroy_listener(server->wl_display, &ipc_display_destroy);\n\n\tipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket,\n\t\t\tWL_EVENT_READABLE, ipc_handle_connection, server);\n}\n\nstruct sockaddr_un *ipc_user_sockaddr(void) {\n\tstruct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un));\n\tif (ipc_sockaddr == NULL) {\n\t\tsway_abort(\"Can't allocate ipc_sockaddr\");\n\t}\n\n\tipc_sockaddr->sun_family = AF_UNIX;\n\tint path_size = sizeof(ipc_sockaddr->sun_path);\n\n\t// Env var typically set by logind, e.g. \"/run/user/<user-id>\"\n\tconst char *dir = getenv(\"XDG_RUNTIME_DIR\");\n\tif (!dir) {\n\t\tdir = \"/tmp\";\n\t}\n\tif (path_size <= snprintf(ipc_sockaddr->sun_path, path_size,\n\t\t\t\"%s/sway-ipc.%u.%i.sock\", dir, getuid(), getpid())) {\n\t\tsway_abort(\"Socket path won't fit into ipc_sockaddr->sun_path\");\n\t}\n\n\treturn ipc_sockaddr;\n}\n\nint ipc_handle_connection(int fd, uint32_t mask, void *data) {\n\t(void) fd;\n\tstruct sway_server *server = data;\n\tassert(mask == WL_EVENT_READABLE);\n\n\tint client_fd = accept(ipc_socket, NULL, NULL);\n\tif (client_fd == -1) {\n\t\tsway_log_errno(SWAY_ERROR, \"Unable to accept IPC client connection\");\n\t\treturn 0;\n\t}\n\n\tint flags;\n\tif ((flags = fcntl(client_fd, F_GETFD)) == -1\n\t\t\t|| fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {\n\t\tsway_log_errno(SWAY_ERROR, \"Unable to set CLOEXEC on IPC client socket\");\n\t\tclose(client_fd);\n\t\treturn 0;\n\t}\n\tif ((flags = fcntl(client_fd, F_GETFL)) == -1\n\t\t\t|| fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {\n\t\tsway_log_errno(SWAY_ERROR, \"Unable to set NONBLOCK on IPC client socket\");\n\t\tclose(client_fd);\n\t\treturn 0;\n\t}\n\n\tstruct ipc_client *client = malloc(sizeof(struct ipc_client));\n\tif (!client) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate ipc client\");\n\t\tclose(client_fd);\n\t\treturn 0;\n\t}\n\tclient->server = server;\n\tclient->pending_length = 0;\n\tclient->fd = client_fd;\n\tclient->subscribed_events = 0;\n\tclient->event_source = wl_event_loop_add_fd(server->wl_event_loop,\n\t\t\tclient_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client);\n\tclient->writable_event_source = NULL;\n\n\tclient->write_buffer_size = 128;\n\tclient->write_buffer_len = 0;\n\tclient->write_buffer = malloc(client->write_buffer_size);\n\tif (!client->write_buffer) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate ipc client write buffer\");\n\t\tclose(client_fd);\n\t\treturn 0;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"New client: fd %d\", client_fd);\n\tlist_add(ipc_client_list, client);\n\treturn 0;\n}\n\nint ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {\n\tstruct ipc_client *client = data;\n\n\tif (mask & WL_EVENT_ERROR) {\n\t\tsway_log(SWAY_ERROR, \"IPC Client socket error, removing client\");\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\tif (mask & WL_EVENT_HANGUP) {\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\tint read_available;\n\tif (ioctl(client_fd, FIONREAD, &read_available) == -1) {\n\t\tsway_log_errno(SWAY_INFO, \"Unable to read IPC socket buffer size\");\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\t// Wait for the rest of the command payload in case the header has already been read\n\tif (client->pending_length > 0) {\n\t\tif ((uint32_t)read_available >= client->pending_length) {\n\t\t\t// Reset pending values.\n\t\t\tuint32_t pending_length = client->pending_length;\n\t\t\tenum ipc_command_type pending_type = client->pending_type;\n\t\t\tclient->pending_length = 0;\n\t\t\tipc_client_handle_command(client, pending_length, pending_type);\n\t\t}\n\t\treturn 0;\n\t}\n\n\tif (read_available < (int) IPC_HEADER_SIZE) {\n\t\treturn 0;\n\t}\n\n\tuint8_t buf[IPC_HEADER_SIZE];\n\t// Should be fully available, because read_available >= IPC_HEADER_SIZE\n\tssize_t received = recv(client_fd, buf, IPC_HEADER_SIZE, 0);\n\tif (received == -1) {\n\t\tsway_log_errno(SWAY_INFO, \"Unable to receive header from IPC client\");\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\tif (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {\n\t\tsway_log(SWAY_DEBUG, \"IPC header check failed\");\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\tmemcpy(&client->pending_length, buf + sizeof(ipc_magic), sizeof(uint32_t));\n\tmemcpy(&client->pending_type, buf + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t));\n\n\tif (read_available - received >= (long)client->pending_length) {\n\t\t// Reset pending values.\n\t\tuint32_t pending_length = client->pending_length;\n\t\tenum ipc_command_type pending_type = client->pending_type;\n\t\tclient->pending_length = 0;\n\t\tipc_client_handle_command(client, pending_length, pending_type);\n\t}\n\n\treturn 0;\n}\n\nstatic bool ipc_has_event_listeners(enum ipc_command_type event) {\n\tfor (int i = 0; i < ipc_client_list->length; i++) {\n\t\tstruct ipc_client *client = ipc_client_list->items[i];\n\t\tif ((client->subscribed_events & event_mask(event)) != 0) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic void ipc_send_event(const char *json_string, enum ipc_command_type event) {\n\tstruct ipc_client *client;\n\tfor (int i = 0; i < ipc_client_list->length; i++) {\n\t\tclient = ipc_client_list->items[i];\n\t\tif ((client->subscribed_events & event_mask(event)) == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!ipc_send_reply(client, event, json_string,\n\t\t\t\t(uint32_t)strlen(json_string))) {\n\t\t\tsway_log_errno(SWAY_INFO, \"Unable to send reply to IPC client\");\n\t\t\t/* ipc_send_reply destroys client on error, which also\n\t\t\t * removes it from the list, so we need to process\n\t\t\t * current index again */\n\t\t\ti--;\n\t\t}\n\t}\n}\n\nvoid ipc_event_workspace(struct sway_workspace *old,\n\t\tstruct sway_workspace *new, const char *change) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending workspace::%s event\", change);\n\tjson_object *obj = json_object_new_object();\n\tjson_object_object_add(obj, \"change\", json_object_new_string(change));\n\tif (old) {\n\t\tjson_object_object_add(obj, \"old\",\n\t\t\t\tipc_json_describe_node_recursive(&old->node));\n\t} else {\n\t\tjson_object_object_add(obj, \"old\", NULL);\n\t}\n\n\tif (new) {\n\t\tjson_object_object_add(obj, \"current\",\n\t\t\t\tipc_json_describe_node_recursive(&new->node));\n\t} else {\n\t\tjson_object_object_add(obj, \"current\", NULL);\n\t}\n\n\tconst char *json_string = json_object_to_json_string(obj);\n\tipc_send_event(json_string, IPC_EVENT_WORKSPACE);\n\tjson_object_put(obj);\n}\n\nvoid ipc_event_window(struct sway_container *window, const char *change) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending window::%s event\", change);\n\tjson_object *obj = json_object_new_object();\n\tjson_object_object_add(obj, \"change\", json_object_new_string(change));\n\tjson_object_object_add(obj, \"container\",\n\t\t\tipc_json_describe_node_recursive(&window->node));\n\n\tconst char *json_string = json_object_to_json_string(obj);\n\tipc_send_event(json_string, IPC_EVENT_WINDOW);\n\tjson_object_put(obj);\n}\n\nvoid ipc_event_barconfig_update(struct bar_config *bar) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending barconfig_update event\");\n\tjson_object *json = ipc_json_describe_bar_config(bar);\n\n\tconst char *json_string = json_object_to_json_string(json);\n\tipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);\n\tjson_object_put(json);\n}\n\nvoid ipc_event_bar_state_update(struct bar_config *bar) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_BAR_STATE_UPDATE)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending bar_state_update event\");\n\n\tjson_object *json = json_object_new_object();\n\tjson_object_object_add(json, \"id\", json_object_new_string(bar->id));\n\tjson_object_object_add(json, \"visible_by_modifier\",\n\t\t\tjson_object_new_boolean(bar->visible_by_modifier));\n\n\tconst char *json_string = json_object_to_json_string(json);\n\tipc_send_event(json_string, IPC_EVENT_BAR_STATE_UPDATE);\n\tjson_object_put(json);\n}\n\nvoid ipc_event_mode(const char *mode, bool pango) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_MODE)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending mode::%s event\", mode);\n\tjson_object *obj = json_object_new_object();\n\tjson_object_object_add(obj, \"change\", json_object_new_string(mode));\n\tjson_object_object_add(obj, \"pango_markup\",\n\t\t\tjson_object_new_boolean(pango));\n\n\tconst char *json_string = json_object_to_json_string(obj);\n\tipc_send_event(json_string, IPC_EVENT_MODE);\n\tjson_object_put(obj);\n}\n\nvoid ipc_event_shutdown(const char *reason) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_SHUTDOWN)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending shutdown::%s event\", reason);\n\n\tjson_object *json = json_object_new_object();\n\tjson_object_object_add(json, \"change\", json_object_new_string(reason));\n\n\tconst char *json_string = json_object_to_json_string(json);\n\tipc_send_event(json_string, IPC_EVENT_SHUTDOWN);\n\tjson_object_put(json);\n}\n\nvoid ipc_event_binding(struct sway_binding *binding) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_BINDING)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending binding event\");\n\n\tjson_object *json_binding = json_object_new_object();\n\tjson_object_object_add(json_binding, \"command\", json_object_new_string(binding->command));\n\n\tconst char *names[10];\n\tint len = get_modifier_names(names, binding->modifiers);\n\tjson_object *modifiers = json_object_new_array();\n\tfor (int i = 0; i < len; ++i) {\n\t\tjson_object_array_add(modifiers, json_object_new_string(names[i]));\n\t}\n\tjson_object_object_add(json_binding, \"event_state_mask\", modifiers);\n\n\tjson_object *input_codes = json_object_new_array();\n\tint input_code = 0;\n\tjson_object *symbols = json_object_new_array();\n\tjson_object *symbol = NULL;\n\n\tswitch (binding->type) {\n\tcase BINDING_KEYCODE:; // bindcode: populate input_codes\n\t\tuint32_t keycode;\n\t\tfor (int i = 0; i < binding->keys->length; ++i) {\n\t\t\tkeycode = *(uint32_t *)binding->keys->items[i];\n\t\t\tjson_object_array_add(input_codes, json_object_new_int(keycode));\n\t\t\tif (i == 0) {\n\t\t\t\tinput_code = keycode;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase BINDING_KEYSYM:\n\tcase BINDING_MOUSESYM:\n\tcase BINDING_MOUSECODE:; // bindsym/mouse: populate symbols\n\t\tuint32_t keysym;\n\t\tchar buffer[64];\n\t\tfor (int i = 0; i < binding->keys->length; ++i) {\n\t\t\tkeysym = *(uint32_t *)binding->keys->items[i];\n\t\t\tif (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) {\n\t\t\t\tsnprintf(buffer, 64, \"button%u\", keysym - BTN_LEFT + 1);\n\t\t\t} else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tjson_object *str = json_object_new_string(buffer);\n\t\t\tif (i == 0) {\n\t\t\t\t// str is owned by both symbol and symbols. Make sure\n\t\t\t\t// to bump the ref count.\n\t\t\t\tjson_object_array_add(symbols, json_object_get(str));\n\t\t\t\tsymbol = str;\n\t\t\t} else {\n\t\t\t\tjson_object_array_add(symbols, str);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tsway_log(SWAY_DEBUG, \"Unsupported ipc binding event\");\n\t\tjson_object_put(input_codes);\n\t\tjson_object_put(symbols);\n\t\tjson_object_put(json_binding);\n\t\treturn; // do not send any event\n\t}\n\n\tjson_object_object_add(json_binding, \"input_codes\", input_codes);\n\tjson_object_object_add(json_binding, \"input_code\", json_object_new_int(input_code));\n\tjson_object_object_add(json_binding, \"symbols\", symbols);\n\tjson_object_object_add(json_binding, \"symbol\", symbol);\n\n\tbool mouse = binding->type == BINDING_MOUSECODE ||\n\t\tbinding->type == BINDING_MOUSESYM;\n\tjson_object_object_add(json_binding, \"input_type\", mouse\n\t\t\t? json_object_new_string(\"mouse\")\n\t\t\t: json_object_new_string(\"keyboard\"));\n\n\tjson_object *json = json_object_new_object();\n\tjson_object_object_add(json, \"change\", json_object_new_string(\"run\"));\n\tjson_object_object_add(json, \"binding\", json_binding);\n\tconst char *json_string = json_object_to_json_string(json);\n\tipc_send_event(json_string, IPC_EVENT_BINDING);\n\tjson_object_put(json);\n}\n\nstatic void ipc_event_tick(const char *payload) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_TICK)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending tick event\");\n\n\tjson_object *json = json_object_new_object();\n\tjson_object_object_add(json, \"first\", json_object_new_boolean(false));\n\tjson_object_object_add(json, \"payload\", json_object_new_string(payload));\n\n\tconst char *json_string = json_object_to_json_string(json);\n\tipc_send_event(json_string, IPC_EVENT_TICK);\n\tjson_object_put(json);\n}\n\nvoid ipc_event_input(const char *change, struct sway_input_device *device) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_INPUT)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending input event\");\n\n\tjson_object *json = json_object_new_object();\n\tjson_object_object_add(json, \"change\", json_object_new_string(change));\n\tjson_object_object_add(json, \"input\", ipc_json_describe_input(device));\n\n\tconst char *json_string = json_object_to_json_string(json);\n\tipc_send_event(json_string, IPC_EVENT_INPUT);\n\tjson_object_put(json);\n}\n\nvoid ipc_event_output(void) {\n\tif (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Sending output event\");\n\n\tjson_object *json = json_object_new_object();\n\tjson_object_object_add(json, \"change\", json_object_new_string(\"unspecified\"));\n\n\tconst char *json_string = json_object_to_json_string(json);\n\tipc_send_event(json_string, IPC_EVENT_OUTPUT);\n\tjson_object_put(json);\n}\n\nint ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {\n\tstruct ipc_client *client = data;\n\n\tif (mask & WL_EVENT_ERROR) {\n\t\tsway_log(SWAY_ERROR, \"IPC Client socket error, removing client\");\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\tif (mask & WL_EVENT_HANGUP) {\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\tif (client->write_buffer_len <= 0) {\n\t\treturn 0;\n\t}\n\n\tssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);\n\n\tif (written == -1 && errno == EAGAIN) {\n\t\treturn 0;\n\t} else if (written == -1) {\n\t\tsway_log_errno(SWAY_INFO, \"Unable to send data from queue to IPC client\");\n\t\tipc_client_disconnect(client);\n\t\treturn 0;\n\t}\n\n\tmemmove(client->write_buffer, client->write_buffer + written, client->write_buffer_len - written);\n\tclient->write_buffer_len -= written;\n\n\tif (client->write_buffer_len == 0 && client->writable_event_source) {\n\t\twl_event_source_remove(client->writable_event_source);\n\t\tclient->writable_event_source = NULL;\n\t}\n\n\treturn 0;\n}\n\nvoid ipc_client_disconnect(struct ipc_client *client) {\n\tif (!sway_assert(client != NULL, \"client != NULL\")) {\n\t\treturn;\n\t}\n\n\tshutdown(client->fd, SHUT_RDWR);\n\n\tsway_log(SWAY_INFO, \"IPC Client %d disconnected\", client->fd);\n\twl_event_source_remove(client->event_source);\n\tif (client->writable_event_source) {\n\t\twl_event_source_remove(client->writable_event_source);\n\t}\n\tint i = 0;\n\twhile (i < ipc_client_list->length && ipc_client_list->items[i] != client) {\n\t\ti++;\n\t}\n\tlist_del(ipc_client_list, i);\n\tfree(client->write_buffer);\n\tclose(client->fd);\n\tfree(client);\n}\n\nstatic void ipc_get_workspaces_callback(struct sway_workspace *workspace,\n\t\tvoid *data) {\n\tjson_object *workspace_json = ipc_json_describe_node(&workspace->node);\n\t// override the default focused indicator because\n\t// it's set differently for the get_workspaces reply\n\tstruct sway_seat *seat = input_manager_get_default_seat();\n\tstruct sway_workspace *focused_ws = seat_get_focused_workspace(seat);\n\tbool focused = workspace == focused_ws;\n\tjson_object_object_del(workspace_json, \"focused\");\n\tjson_object_object_add(workspace_json, \"focused\",\n\t\t\tjson_object_new_boolean(focused));\n\tjson_object_array_add((json_object *)data, workspace_json);\n\n\tfocused_ws = output_get_active_workspace(workspace->output);\n\tbool visible = workspace == focused_ws;\n\tjson_object_object_add(workspace_json, \"visible\",\n\t\t\tjson_object_new_boolean(visible));\n}\n\nstatic void ipc_get_marks_callback(struct sway_container *con, void *data) {\n\tjson_object *marks = (json_object *)data;\n\tfor (int i = 0; i < con->marks->length; ++i) {\n\t\tchar *mark = (char *)con->marks->items[i];\n\t\tjson_object_array_add(marks, json_object_new_string(mark));\n\t}\n}\n\nvoid ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length,\n\t\tenum ipc_command_type payload_type) {\n\tif (!sway_assert(client != NULL, \"client != NULL\")) {\n\t\treturn;\n\t}\n\n\tchar *buf = malloc(payload_length + 1);\n\tif (!buf) {\n\t\tsway_log_errno(SWAY_INFO, \"Unable to allocate IPC payload\");\n\t\tipc_client_disconnect(client);\n\t\treturn;\n\t}\n\tif (payload_length > 0) {\n\t\t// Payload should be fully available\n\t\tssize_t received = recv(client->fd, buf, payload_length, 0);\n\t\tif (received == -1)\n\t\t{\n\t\t\tsway_log_errno(SWAY_INFO, \"Unable to receive payload from IPC client\");\n\t\t\tipc_client_disconnect(client);\n\t\t\tfree(buf);\n\t\t\treturn;\n\t\t}\n\t}\n\tbuf[payload_length] = '\\0';\n\n\tswitch (payload_type) {\n\tcase IPC_COMMAND:\n\t{\n\t\tchar *line = strtok(buf, \"\\n\");\n\t\twhile (line) {\n\t\t\tsize_t line_length = strlen(line);\n\t\t\tif (line + line_length >= buf + payload_length) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tline[line_length] = ';';\n\t\t\tline = strtok(NULL, \"\\n\");\n\t\t}\n\n\t\tlist_t *res_list = execute_command(buf, NULL, NULL);\n\t\tif (modeset_is_pending()) {\n\t\t\t// IPC expects commands to have taken immediate effect, so we need\n\t\t\t// to force a modeset after output commands. We do a single modeset\n\t\t\t// here to avoid modesetting for every output command in sequence.\n\t\t\tforce_modeset();\n\t\t}\n\t\ttransaction_commit_dirty();\n\t\tchar *json = cmd_results_to_json(res_list);\n\t\tint length = strlen(json);\n\t\tipc_send_reply(client, payload_type, json, (uint32_t)length);\n\t\tfree(json);\n\t\twhile (res_list->length) {\n\t\t\tstruct cmd_results *results = res_list->items[0];\n\t\t\tfree_cmd_results(results);\n\t\t\tlist_del(res_list, 0);\n\t\t}\n\t\tlist_free(res_list);\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_SEND_TICK:\n\t{\n\t\tipc_event_tick(buf);\n\t\tipc_send_reply(client, payload_type, \"{\\\"success\\\": true}\", 17);\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_OUTPUTS:\n\t{\n\t\tjson_object *outputs = json_object_new_array();\n\t\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tjson_object *output_json = ipc_json_describe_node(&output->node);\n\n\t\t\t// override the default focused indicator because it's set\n\t\t\t// differently for the get_outputs reply\n\t\t\tstruct sway_seat *seat = input_manager_get_default_seat();\n\t\t\tstruct sway_workspace *focused_ws =\n\t\t\t\tseat_get_focused_workspace(seat);\n\t\t\tbool focused = focused_ws && output == focused_ws->output;\n\t\t\tjson_object_object_del(output_json, \"focused\");\n\t\t\tjson_object_object_add(output_json, \"focused\",\n\t\t\t\tjson_object_new_boolean(focused));\n\n\t\t\tconst char *subpixel = sway_wl_output_subpixel_to_string(output->wlr_output->subpixel);\n\t\t\tjson_object_object_add(output_json, \"subpixel_hinting\", json_object_new_string(subpixel));\n\t\t\tjson_object_array_add(outputs, output_json);\n\t\t}\n\t\tstruct sway_output *output;\n\t\twl_list_for_each(output, &root->all_outputs, link) {\n\t\t\tif (!output->enabled && output != root->fallback_output) {\n\t\t\t\tjson_object_array_add(outputs,\n\t\t\t\t\t\tipc_json_describe_disabled_output(output));\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 0; i < root->non_desktop_outputs->length; i++) {\n\t\t\tstruct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i];\n\t\t\tjson_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output));\n\t\t}\n\n\t\tconst char *json_string = json_object_to_json_string(outputs);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(outputs); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_WORKSPACES:\n\t{\n\t\tjson_object *workspaces = json_object_new_array();\n\t\troot_for_each_workspace(ipc_get_workspaces_callback, workspaces);\n\t\tconst char *json_string = json_object_to_json_string(workspaces);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(workspaces); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_SUBSCRIBE:\n\t{\n\t\t// TODO: Check if they're permitted to use these events\n\t\tstruct json_object *request = json_tokener_parse(buf);\n\t\tif (request == NULL || !json_object_is_type(request, json_type_array)) {\n\t\t\tconst char msg[] = \"{\\\"success\\\": false}\";\n\t\t\tipc_send_reply(client, payload_type, msg, strlen(msg));\n\t\t\tsway_log(SWAY_INFO, \"Failed to parse subscribe request\");\n\t\t\tgoto exit_cleanup;\n\t\t}\n\n\t\tbool is_tick = false;\n\t\t// parse requested event types\n\t\tfor (size_t i = 0; i < json_object_array_length(request); i++) {\n\t\t\tconst char *event_type = json_object_get_string(json_object_array_get_idx(request, i));\n\t\t\tif (strcmp(event_type, \"workspace\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);\n\t\t\t} else if (strcmp(event_type, \"output\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_OUTPUT);\n\t\t\t} else if (strcmp(event_type, \"barconfig_update\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);\n\t\t\t} else if (strcmp(event_type, \"bar_state_update\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_BAR_STATE_UPDATE);\n\t\t\t} else if (strcmp(event_type, \"mode\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_MODE);\n\t\t\t} else if (strcmp(event_type, \"shutdown\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);\n\t\t\t} else if (strcmp(event_type, \"window\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_WINDOW);\n\t\t\t} else if (strcmp(event_type, \"binding\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_BINDING);\n\t\t\t} else if (strcmp(event_type, \"tick\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_TICK);\n\t\t\t\tis_tick = true;\n\t\t\t} else if (strcmp(event_type, \"input\") == 0) {\n\t\t\t\tclient->subscribed_events |= event_mask(IPC_EVENT_INPUT);\n\t\t\t} else {\n\t\t\t\tconst char msg[] = \"{\\\"success\\\": false}\";\n\t\t\t\tipc_send_reply(client, payload_type, msg, strlen(msg));\n\t\t\t\tjson_object_put(request);\n\t\t\t\tsway_log(SWAY_INFO, \"Unsupported event type in subscribe request\");\n\t\t\t\tgoto exit_cleanup;\n\t\t\t}\n\t\t}\n\n\t\tjson_object_put(request);\n\t\tconst char msg[] = \"{\\\"success\\\": true}\";\n\t\tipc_send_reply(client, payload_type, msg, strlen(msg));\n\t\tif (is_tick) {\n\t\t\tconst char tickmsg[] = \"{\\\"first\\\": true, \\\"payload\\\": \\\"\\\"}\";\n\t\t\tipc_send_reply(client, IPC_EVENT_TICK, tickmsg,\n\t\t\t\tstrlen(tickmsg));\n\t\t}\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_INPUTS:\n\t{\n\t\tjson_object *inputs = json_object_new_array();\n\t\tstruct sway_input_device *device = NULL;\n\t\twl_list_for_each(device, &server.input->devices, link) {\n\t\t\tjson_object_array_add(inputs, ipc_json_describe_input(device));\n\t\t}\n\t\tconst char *json_string = json_object_to_json_string(inputs);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(inputs); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_SEATS:\n\t{\n\t\tjson_object *seats = json_object_new_array();\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tjson_object_array_add(seats, ipc_json_describe_seat(seat));\n\t\t}\n\t\tconst char *json_string = json_object_to_json_string(seats);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(seats); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_TREE:\n\t{\n\t\tjson_object *tree = ipc_json_describe_node_recursive(&root->node);\n\t\tconst char *json_string = json_object_to_json_string(tree);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(tree);\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_MARKS:\n\t{\n\t\tjson_object *marks = json_object_new_array();\n\t\troot_for_each_container(ipc_get_marks_callback, marks);\n\t\tconst char *json_string = json_object_to_json_string(marks);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(marks);\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_VERSION:\n\t{\n\t\tjson_object *version = ipc_json_get_version();\n\t\tconst char *json_string = json_object_to_json_string(version);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(version); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_BAR_CONFIG:\n\t{\n\t\tif (!buf[0]) {\n\t\t\t// Send list of configured bar IDs\n\t\t\tjson_object *bars = json_object_new_array();\n\t\t\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\t\t\tstruct bar_config *bar = config->bars->items[i];\n\t\t\t\tjson_object_array_add(bars, json_object_new_string(bar->id));\n\t\t\t}\n\t\t\tconst char *json_string = json_object_to_json_string(bars);\n\t\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t\t(uint32_t)strlen(json_string));\n\t\t\tjson_object_put(bars); // free\n\t\t} else {\n\t\t\t// Send particular bar's details\n\t\t\tstruct bar_config *bar = NULL;\n\t\t\tfor (int i = 0; i < config->bars->length; ++i) {\n\t\t\t\tbar = config->bars->items[i];\n\t\t\t\tif (strcmp(buf, bar->id) == 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbar = NULL;\n\t\t\t}\n\t\t\tif (!bar) {\n\t\t\t\tconst char *error = \"{ \\\"success\\\": false, \\\"error\\\": \\\"No bar with that ID\\\" }\";\n\t\t\t\tipc_send_reply(client, payload_type, error,\n\t\t\t\t\t(uint32_t)strlen(error));\n\t\t\t\tgoto exit_cleanup;\n\t\t\t}\n\t\t\tjson_object *json = ipc_json_describe_bar_config(bar);\n\t\t\tconst char *json_string = json_object_to_json_string(json);\n\t\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t\t(uint32_t)strlen(json_string));\n\t\t\tjson_object_put(json); // free\n\t\t}\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_BINDING_MODES:\n\t{\n\t\tjson_object *modes = json_object_new_array();\n\t\tfor (int i = 0; i < config->modes->length; i++) {\n\t\t\tstruct sway_mode *mode = config->modes->items[i];\n\t\t\tjson_object_array_add(modes, json_object_new_string(mode->name));\n\t\t}\n\t\tconst char *json_string = json_object_to_json_string(modes);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(modes); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_BINDING_STATE:\n\t{\n\t\tjson_object *current_mode = ipc_json_get_binding_mode();\n\t\tconst char *json_string = json_object_to_json_string(current_mode);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(current_mode); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_GET_CONFIG:\n\t{\n\t\tjson_object *json = json_object_new_object();\n\t\tjson_object_object_add(json, \"config\", json_object_new_string(config->current_config));\n\t\tconst char *json_string = json_object_to_json_string(json);\n\t\tipc_send_reply(client, payload_type, json_string,\n\t\t\t(uint32_t)strlen(json_string));\n\t\tjson_object_put(json); // free\n\t\tgoto exit_cleanup;\n\t}\n\n\tcase IPC_SYNC:\n\t{\n\t\t// It was decided sway will not support this, just return success:false\n\t\tconst char msg[] = \"{\\\"success\\\": false}\";\n\t\tipc_send_reply(client, payload_type, msg, strlen(msg));\n\t\tgoto exit_cleanup;\n\t}\n\n\tdefault:\n\t\tsway_log(SWAY_INFO, \"Unknown IPC command type %x\", payload_type);\n\t\tgoto exit_cleanup;\n\t}\n\nexit_cleanup:\n\tfree(buf);\n}\n\nbool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,\n\t\tconst char *payload, uint32_t payload_length) {\n\tassert(payload);\n\n\tchar data[IPC_HEADER_SIZE];\n\n\tmemcpy(data, ipc_magic, sizeof(ipc_magic));\n\tmemcpy(data + sizeof(ipc_magic), &payload_length, sizeof(payload_length));\n\tmemcpy(data + sizeof(ipc_magic) + sizeof(payload_length), &payload_type, sizeof(payload_type));\n\n\twhile (client->write_buffer_len + IPC_HEADER_SIZE + payload_length >=\n\t\t\t\t client->write_buffer_size) {\n\t\tclient->write_buffer_size *= 2;\n\t}\n\n\tif (client->write_buffer_size > 4e6) { // 4 MB\n\t\tsway_log(SWAY_ERROR, \"Client write buffer too big (%zu), disconnecting client\",\n\t\t\t\tclient->write_buffer_size);\n\t\tipc_client_disconnect(client);\n\t\treturn false;\n\t}\n\n\tchar *new_buffer = realloc(client->write_buffer, client->write_buffer_size);\n\tif (!new_buffer) {\n\t\tsway_log(SWAY_ERROR, \"Unable to reallocate ipc client write buffer\");\n\t\tipc_client_disconnect(client);\n\t\treturn false;\n\t}\n\tclient->write_buffer = new_buffer;\n\n\tmemcpy(client->write_buffer + client->write_buffer_len, data, IPC_HEADER_SIZE);\n\tclient->write_buffer_len += IPC_HEADER_SIZE;\n\tmemcpy(client->write_buffer + client->write_buffer_len, payload, payload_length);\n\tclient->write_buffer_len += payload_length;\n\n\tif (!client->writable_event_source) {\n\t\tclient->writable_event_source = wl_event_loop_add_fd(\n\t\t\t\tserver.wl_event_loop, client->fd, WL_EVENT_WRITABLE,\n\t\t\t\tipc_client_handle_writable, client);\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "sway/lock.c",
    "content": "#include <assert.h>\n#include <wlr/types/wlr_scene.h>\n#include <wlr/types/wlr_session_lock_v1.h>\n#include \"log.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/keyboard.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/layers.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/lock.h\"\n\nstruct sway_session_lock_output {\n\tstruct wlr_scene_tree *tree;\n\tstruct wlr_scene_rect *background;\n\tstruct sway_session_lock *lock;\n\n\tstruct sway_output *output;\n\n\tstruct wl_list link; // sway_session_lock::outputs\n\n\tstruct wl_listener destroy;\n\n\tstruct wlr_session_lock_surface_v1 *surface;\n\n\t// invalid if surface is NULL\n\tstruct wl_listener surface_destroy;\n\tstruct wl_listener surface_map;\n};\n\nstatic void focus_surface(struct sway_session_lock *lock,\n\t\tstruct wlr_surface *focused) {\n\tlock->focused = focused;\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat_set_focus_surface(seat, focused, false);\n\t}\n}\n\nstatic void refocus_output(struct sway_session_lock_output *output) {\n\t// Move the seat focus to another surface if one is available\n\tif (output->lock->focused == output->surface->surface) {\n\t\tstruct wlr_surface *next_focus = NULL;\n\n\t\tstruct sway_session_lock_output *candidate;\n\t\twl_list_for_each(candidate, &output->lock->outputs, link) {\n\t\t\tif (candidate == output || !candidate->surface) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (candidate->surface->surface->mapped) {\n\t\t\t\tnext_focus = candidate->surface->surface;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfocus_surface(output->lock, next_focus);\n\t}\n}\n\nstatic void handle_surface_map(struct wl_listener *listener, void *data) {\n\tstruct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map);\n\tif (surf->lock->focused == NULL) {\n\t\tfocus_surface(surf->lock, surf->surface->surface);\n\t}\n\tcursor_rebase_all();\n}\n\nstatic void handle_surface_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_session_lock_output *output =\n\t\twl_container_of(listener, output, surface_destroy);\n\trefocus_output(output);\n\n\tsway_assert(output->surface, \"Trying to destroy a surface that the lock doesn't think exists\");\n\toutput->surface = NULL;\n\twl_list_remove(&output->surface_destroy.link);\n\twl_list_remove(&output->surface_map.link);\n}\n\nstatic void lock_output_reconfigure(struct sway_session_lock_output *output) {\n\tint width = output->output->width;\n\tint height = output->output->height;\n\n\twlr_scene_rect_set_size(output->background, width, height);\n\n\tif (output->surface) {\n\t\twlr_session_lock_surface_v1_configure(output->surface, width, height);\n\t}\n}\n\nvoid arrange_locks(void) {\n\tif (server.session_lock.lock == NULL) {\n\t\treturn;\n\t}\n\n\tstruct sway_session_lock_output *lock_output;\n\twl_list_for_each(lock_output, &server.session_lock.lock->outputs, link) {\n\t\tlock_output_reconfigure(lock_output);\n\t}\n}\n\nstatic void handle_new_surface(struct wl_listener *listener, void *data) {\n\tstruct sway_session_lock *lock = wl_container_of(listener, lock, new_surface);\n\tstruct wlr_session_lock_surface_v1 *lock_surface = data;\n\tstruct sway_output *output = lock_surface->output->data;\n\n\tsway_log(SWAY_DEBUG, \"new lock layer surface\");\n\n\tstruct sway_session_lock_output *current_lock_output, *lock_output = NULL;\n\twl_list_for_each(current_lock_output, &lock->outputs, link) {\n\t\tif (current_lock_output->output == output) {\n\t\t\tlock_output = current_lock_output;\n\t\t\tbreak;\n\t\t}\n\t}\n\tsway_assert(lock_output, \"Couldn't find output to lock\");\n\tsway_assert(!lock_output->surface, \"Tried to reassign a surface to an existing output\");\n\n\tlock_output->surface = lock_surface;\n\n\twlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface);\n\n\tlock_output->surface_destroy.notify = handle_surface_destroy;\n\twl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy);\n\tlock_output->surface_map.notify = handle_surface_map;\n\twl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map);\n\n\tlock_output_reconfigure(lock_output);\n}\n\nstatic void sway_session_lock_output_destroy(struct sway_session_lock_output *output) {\n\tif (output->surface) {\n\t\trefocus_output(output);\n\t\twl_list_remove(&output->surface_destroy.link);\n\t\twl_list_remove(&output->surface_map.link);\n\t}\n\n\twl_list_remove(&output->destroy.link);\n\twl_list_remove(&output->link);\n\n\tfree(output);\n}\n\nstatic void lock_node_handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct sway_session_lock_output *output =\n\t\twl_container_of(listener, output, destroy);\n\tsway_session_lock_output_destroy(output);\n}\n\nstatic struct sway_session_lock_output *session_lock_output_create(\n\t\tstruct sway_session_lock *lock, struct sway_output *output) {\n\tstruct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output));\n\tif (!lock_output) {\n\t\tsway_log(SWAY_ERROR, \"failed to allocate a session lock output\");\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock);\n\tif (!tree) {\n\t\tsway_log(SWAY_ERROR, \"failed to allocate a session lock output scene tree\");\n\t\tfree(lock_output);\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){\n\t\tlock->abandoned ? 1.f : 0.f,\n\t\t0.f,\n\t\t0.f,\n\t\t1.f,\n\t});\n\tif (!background) {\n\t\tsway_log(SWAY_ERROR, \"failed to allocate a session lock output scene background\");\n\t\twlr_scene_node_destroy(&tree->node);\n\t\tfree(lock_output);\n\t\treturn NULL;\n\t}\n\n\tlock_output->output = output;\n\tlock_output->tree = tree;\n\tlock_output->background = background;\n\tlock_output->lock = lock;\n\n\tlock_output->destroy.notify = lock_node_handle_destroy;\n\twl_signal_add(&tree->node.events.destroy, &lock_output->destroy);\n\n\tlock_output_reconfigure(lock_output);\n\n\twl_list_insert(&lock->outputs, &lock_output->link);\n\n\treturn lock_output;\n}\n\nstatic void sway_session_lock_destroy(struct sway_session_lock* lock) {\n\tstruct sway_session_lock_output *lock_output, *tmp_lock_output;\n\twl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) {\n\t\t// destroying the node will also destroy the whole lock output\n\t\twlr_scene_node_destroy(&lock_output->tree->node);\n\t}\n\n\tif (server.session_lock.lock == lock) {\n\t\tserver.session_lock.lock = NULL;\n\t}\n\n\tif (!lock->abandoned) {\n\t\twl_list_remove(&lock->destroy.link);\n\t\twl_list_remove(&lock->unlock.link);\n\t\twl_list_remove(&lock->new_surface.link);\n\t}\n\n\tfree(lock);\n}\n\nstatic void handle_unlock(struct wl_listener *listener, void *data) {\n\tstruct sway_session_lock *lock = wl_container_of(listener, lock, unlock);\n\tsway_log(SWAY_DEBUG, \"session unlocked\");\n\n\tsway_session_lock_destroy(lock);\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t// copied from seat_set_focus_layer -- deduplicate?\n\t\tstruct sway_node *previous = seat_get_focus_inactive(seat, &root->node);\n\t\tif (previous) {\n\t\t\t// Hack to get seat to re-focus the return value of get_focus\n\t\t\tseat_set_focus(seat, NULL);\n\t\t\tseat_set_focus(seat, previous);\n\t\t}\n\t}\n\n\t// Triggers a refocus of the topmost surface layer if necessary\n\t// TODO: Make layer surface focus per-output based on cursor position\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tarrange_layers(output);\n\t}\n\n\t// Views are now visible, so check if we need to activate inhibition again.\n\tsway_idle_inhibit_v1_check_active();\n}\n\nstatic void handle_abandon(struct wl_listener *listener, void *data) {\n\tstruct sway_session_lock *lock = wl_container_of(listener, lock, destroy);\n\tsway_log(SWAY_INFO, \"session lock abandoned\");\n\n\tstruct sway_session_lock_output *lock_output;\n\twl_list_for_each(lock_output, &lock->outputs, link) {\n\t\twlr_scene_rect_set_color(lock_output->background,\n\t\t\t(float[4]){ 1.f, 0.f, 0.f, 1.f });\n\t}\n\n\tlock->abandoned = true;\n\twl_list_remove(&lock->destroy.link);\n\twl_list_remove(&lock->unlock.link);\n\twl_list_remove(&lock->new_surface.link);\n}\n\nstatic void handle_session_lock(struct wl_listener *listener, void *data) {\n\tstruct wlr_session_lock_v1 *lock = data;\n\tstruct wl_client *client = wl_resource_get_client(lock->resource);\n\n\tif (server.session_lock.lock) {\n\t\tif (server.session_lock.lock->abandoned) {\n\t\t\tsway_log(SWAY_INFO, \"Replacing abandoned lock\");\n\t\t\tsway_session_lock_destroy(server.session_lock.lock);\n\t\t} else {\n\t\t\tsway_log(SWAY_ERROR, \"Cannot lock an already locked session\");\n\t\t\twlr_session_lock_v1_destroy(lock);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tstruct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock));\n\tif (!sway_lock) {\n\t\tsway_log(SWAY_ERROR, \"failed to allocate a session lock object\");\n\t\twlr_session_lock_v1_destroy(lock);\n\t\treturn;\n\t}\n\n\twl_list_init(&sway_lock->outputs);\n\n\tsway_log(SWAY_DEBUG, \"session locked\");\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat_unfocus_unless_client(seat, client);\n\t}\n\n\tstruct sway_output *output;\n\twl_list_for_each(output, &root->all_outputs, link) {\n\t\tsway_session_lock_add_output(sway_lock, output);\n\t}\n\n\tsway_lock->new_surface.notify = handle_new_surface;\n\twl_signal_add(&lock->events.new_surface, &sway_lock->new_surface);\n\tsway_lock->unlock.notify = handle_unlock;\n\twl_signal_add(&lock->events.unlock, &sway_lock->unlock);\n\tsway_lock->destroy.notify = handle_abandon;\n\twl_signal_add(&lock->events.destroy, &sway_lock->destroy);\n\n\twlr_session_lock_v1_send_locked(lock);\n\tserver.session_lock.lock = sway_lock;\n\n\t// The lock screen covers everything, so check if any active inhibition got\n\t// deactivated due to lost visibility.\n\tsway_idle_inhibit_v1_check_active();\n}\n\nstatic void handle_session_lock_destroy(struct wl_listener *listener, void *data) {\n\t// if the server shuts down while a lock is active, destroy the lock\n\tif (server.session_lock.lock) {\n\t\tsway_session_lock_destroy(server.session_lock.lock);\n\t}\n\n\twl_list_remove(&server.session_lock.new_lock.link);\n\twl_list_remove(&server.session_lock.manager_destroy.link);\n\n\tserver.session_lock.manager = NULL;\n}\n\nvoid sway_session_lock_add_output(struct sway_session_lock *lock,\n\t\tstruct sway_output *output) {\n\tstruct sway_session_lock_output *lock_output =\n\t\tsession_lock_output_create(lock, output);\n\n\t// if we run out of memory while trying to lock the screen, the best we\n\t// can do is kill the sway process. Security conscious users will have\n\t// the sway session fall back to a login shell.\n\tif (!lock_output) {\n\t\tsway_log(SWAY_ERROR, \"aborting: failed to allocate a lock output\");\n\t\tabort();\n\t}\n}\n\nbool sway_session_lock_has_surface(struct sway_session_lock *lock,\n\t\tstruct wlr_surface *surface) {\n\tstruct sway_session_lock_output *lock_output;\n\twl_list_for_each(lock_output, &lock->outputs, link) {\n\t\tif (lock_output->surface && lock_output->surface->surface == surface) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid sway_session_lock_init(void) {\n\tserver.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);\n\n\tserver.session_lock.new_lock.notify = handle_session_lock;\n\tserver.session_lock.manager_destroy.notify = handle_session_lock_destroy;\n\twl_signal_add(&server.session_lock.manager->events.new_lock,\n\t\t&server.session_lock.new_lock);\n\twl_signal_add(&server.session_lock.manager->events.destroy,\n\t\t&server.session_lock.manager_destroy);\n}\n"
  },
  {
    "path": "sway/main.c",
    "content": "#include <getopt.h>\n#include <pango/pangocairo.h>\n#include <pthread.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/resource.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <sys/un.h>\n#include <unistd.h>\n#include <wlr/util/log.h>\n#include <wlr/version.h>\n#include \"sway/commands.h\"\n#include \"sway/config.h\"\n#include \"sway/server.h\"\n#include \"sway/swaynag.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/ipc-server.h\"\n#include \"ipc-client.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nstatic bool terminate_request = false;\nstatic int exit_value = 0;\nstatic struct rlimit original_nofile_rlimit = {0};\nstruct sway_server server = {0};\nstruct sway_debug debug = {0};\n\nvoid sway_terminate(int exit_code) {\n\tif (!server.wl_display) {\n\t\t// Running as IPC client\n\t\texit(exit_code);\n\t} else {\n\t\t// Running as server\n\t\tterminate_request = true;\n\t\texit_value = exit_code;\n\t\tipc_event_shutdown(\"exit\");\n\t\twl_display_terminate(server.wl_display);\n\t}\n}\n\nvoid run_as_ipc_client(char *command, char *socket_path) {\n\tint socketfd = ipc_open_socket(socket_path);\n\tuint32_t len = strlen(command);\n\tchar *resp = ipc_single_command(socketfd, IPC_COMMAND, command, &len);\n\tprintf(\"%s\\n\", resp);\n\tfree(resp);\n\tclose(socketfd);\n}\n\nstatic void log_env(void) {\n\tconst char *log_vars[] = {\n\t\t\"LD_LIBRARY_PATH\",\n\t\t\"LD_PRELOAD\",\n\t\t\"PATH\",\n\t\t\"SWAYSOCK\",\n\t};\n\tfor (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) {\n\t\tchar *value = getenv(log_vars[i]);\n\t\tsway_log(SWAY_INFO, \"%s=%s\", log_vars[i], value != NULL ? value : \"\");\n\t}\n}\n\nstatic void log_file(FILE *f) {\n\tchar *line = NULL;\n\tsize_t line_size = 0;\n\tssize_t nread;\n\twhile ((nread = getline(&line, &line_size, f)) != -1) {\n\t\tif (line[nread - 1] == '\\n') {\n\t\t\tline[nread - 1] = '\\0';\n\t\t}\n\t\tsway_log(SWAY_INFO, \"%s\", line);\n\t}\n\tfree(line);\n}\n\nstatic void log_distro(void) {\n\tconst char *paths[] = {\n\t\t\"/etc/lsb-release\",\n\t\t\"/etc/os-release\",\n\t\t\"/etc/debian_version\",\n\t\t\"/etc/redhat-release\",\n\t\t\"/etc/gentoo-release\",\n\t};\n\tfor (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) {\n\t\tFILE *f = fopen(paths[i], \"r\");\n\t\tif (f) {\n\t\t\tsway_log(SWAY_INFO, \"Contents of %s:\", paths[i]);\n\t\t\tlog_file(f);\n\t\t\tfclose(f);\n\t\t}\n\t}\n}\n\nstatic void log_kernel(void) {\n\tFILE *f = popen(\"uname -a\", \"r\");\n\tif (!f) {\n\t\tsway_log(SWAY_INFO, \"Unable to determine kernel version\");\n\t\treturn;\n\t}\n\tlog_file(f);\n\tpclose(f);\n}\n\nstatic void restore_nofile_limit(void) {\n\tif (original_nofile_rlimit.rlim_cur == 0) {\n\t\treturn;\n\t}\n\tif (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"Failed to restore max open files limit: \"\n\t\t\t\"setrlimit(NOFILE) failed\");\n\t}\n}\n\nstatic void increase_nofile_limit(void) {\n\tif (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"Failed to bump max open files limit: \"\n\t\t\t\"getrlimit(NOFILE) failed\");\n\t\treturn;\n\t}\n\n\tstruct rlimit new_rlimit = original_nofile_rlimit;\n\tnew_rlimit.rlim_cur = new_rlimit.rlim_max;\n\tif (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"Failed to bump max open files limit: \"\n\t\t\t\"setrlimit(NOFILE) failed\");\n\t\tsway_log(SWAY_INFO, \"Running with %d max open files\",\n\t\t\t(int)original_nofile_rlimit.rlim_cur);\n\t\treturn;\n\t}\n\n\tpthread_atfork(NULL, NULL, restore_nofile_limit);\n}\n\nstatic int term_signal(int signal, void *data) {\n\tsway_terminate(EXIT_SUCCESS);\n\treturn 0;\n}\n\nstatic void restore_signals(void) {\n\tsigset_t set;\n\tsigemptyset(&set);\n\tsigprocmask(SIG_SETMASK, &set, NULL);\n\n\tstruct sigaction sa_dfl = { .sa_handler = SIG_DFL };\n\tsigaction(SIGCHLD, &sa_dfl, NULL);\n\tsigaction(SIGPIPE, &sa_dfl, NULL);\n}\n\nstatic void init_signals(void) {\n\twl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL);\n\twl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL);\n\n\tstruct sigaction sa_ign = { .sa_handler = SIG_IGN };\n\t// avoid need to reap children\n\tsigaction(SIGCHLD, &sa_ign, NULL);\n\t// prevent ipc write errors from crashing sway\n\tsigaction(SIGPIPE, &sa_ign, NULL);\n\n\tpthread_atfork(NULL, NULL, restore_signals);\n}\n\nvoid enable_debug_flag(const char *flag) {\n\tif (strcmp(flag, \"noatomic\") == 0) {\n\t\tdebug.noatomic = true;\n\t} else if (strcmp(flag, \"txn-wait\") == 0) {\n\t\tdebug.txn_wait = true;\n\t} else if (strcmp(flag, \"txn-timings\") == 0) {\n\t\tdebug.txn_timings = true;\n\t} else if (has_prefix(flag, \"txn-timeout=\")) {\n\t\tserver.txn_timeout_ms = atoi(&flag[strlen(\"txn-timeout=\")]);\n\t} else {\n\t\tsway_log(SWAY_ERROR, \"Unknown debug flag: %s\", flag);\n\t}\n}\n\nstatic sway_log_importance_t convert_wlr_log_importance(\n\t\tenum wlr_log_importance importance) {\n\tswitch (importance) {\n\tcase WLR_ERROR:\n\t\treturn SWAY_ERROR;\n\tcase WLR_INFO:\n\t\treturn SWAY_INFO;\n\tdefault:\n\t\treturn SWAY_DEBUG;\n\t}\n}\n\nstatic void handle_wlr_log(enum wlr_log_importance importance,\n\t\tconst char *fmt, va_list args) {\n\tstatic char sway_fmt[1024];\n\tsnprintf(sway_fmt, sizeof(sway_fmt), \"[wlr] %s\", fmt);\n\t_sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);\n}\n\nstatic const struct option long_options[] = {\n\t{\"help\", no_argument, NULL, 'h'},\n\t{\"config\", required_argument, NULL, 'c'},\n\t{\"validate\", no_argument, NULL, 'C'},\n\t{\"debug\", no_argument, NULL, 'd'},\n\t{\"version\", no_argument, NULL, 'v'},\n\t{\"verbose\", no_argument, NULL, 'V'},\n\t{\"get-socketpath\", no_argument, NULL, 'p'},\n\t{\"unsupported-gpu\", no_argument, NULL, 'u'},\n\t{0, 0, 0, 0}\n};\n\nstatic const char usage[] =\n\t\"Usage: sway [options] [command]\\n\"\n\t\"\\n\"\n\t\"  -h, --help             Show help message and quit.\\n\"\n\t\"  -c, --config <config>  Specify a config file.\\n\"\n\t\"  -C, --validate         Check the validity of the config file, then exit.\\n\"\n\t\"  -d, --debug            Enables full logging, including debug information.\\n\"\n\t\"  -v, --version          Show the version number and quit.\\n\"\n\t\"  -V, --verbose          Enables more verbose logging.\\n\"\n\t\"      --get-socketpath   Gets the IPC socket path and prints it, then exits.\\n\"\n\t\"\\n\";\n\nint main(int argc, char **argv) {\n\tbool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false;\n\n\tchar *config_path = NULL;\n\n\tint c;\n\twhile (1) {\n\t\tint option_index = 0;\n\t\tc = getopt_long(argc, argv, \"hCdD:vVc:\", long_options, &option_index);\n\t\tif (c == -1) {\n\t\t\tbreak;\n\t\t}\n\t\tswitch (c) {\n\t\tcase 'h': // help\n\t\t\tprintf(\"%s\", usage);\n\t\t\texit(EXIT_SUCCESS);\n\t\t\tbreak;\n\t\tcase 'c': // config\n\t\t\tfree(config_path);\n\t\t\tconfig_path = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'C': // validate\n\t\t\tvalidate = true;\n\t\t\tbreak;\n\t\tcase 'd': // debug\n\t\t\tdebug = true;\n\t\t\tbreak;\n\t\tcase 'D': // extended debug options\n\t\t\tenable_debug_flag(optarg);\n\t\t\tbreak;\n\t\tcase 'u':\n\t\t\tallow_unsupported_gpu = true;\n\t\t\tbreak;\n\t\tcase 'v': // version\n\t\t\tprintf(\"sway version \" SWAY_VERSION \"\\n\");\n\t\t\texit(EXIT_SUCCESS);\n\t\t\tbreak;\n\t\tcase 'V': // verbose\n\t\t\tverbose = true;\n\t\t\tbreak;\n\t\tcase 'p': // --get-socketpath\n\t\t\tif (getenv(\"SWAYSOCK\")) {\n\t\t\t\tprintf(\"%s\\n\", getenv(\"SWAYSOCK\"));\n\t\t\t\texit(EXIT_SUCCESS);\n\t\t\t} else {\n\t\t\t\tfprintf(stderr, \"sway socket not detected.\\n\");\n\t\t\t\texit(EXIT_FAILURE);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfprintf(stderr, \"%s\", usage);\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t}\n\n\t// Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the\n\t// clear error message (when not running as an IPC client).\n\tif (!getenv(\"XDG_RUNTIME_DIR\") && optind == argc) {\n\t\tfprintf(stderr,\n\t\t\t\t\"XDG_RUNTIME_DIR is not set in the environment. Aborting.\\n\");\n\t\texit(EXIT_FAILURE);\n\t}\n\n\tchar *unsupported_gpu_env = getenv(\"SWAY_UNSUPPORTED_GPU\");\n\t// we let the flag override the environment variable\n\tif (!allow_unsupported_gpu && unsupported_gpu_env) {\n\t\tallow_unsupported_gpu = parse_boolean(unsupported_gpu_env, false);\n\t}\n\n\t// As the 'callback' function for wlr_log is equivalent to that for\n\t// sway, we do not need to override it.\n\tif (debug) {\n\t\tsway_log_init(SWAY_DEBUG, sway_terminate);\n\t\twlr_log_init(WLR_DEBUG, handle_wlr_log);\n\t} else if (verbose) {\n\t\tsway_log_init(SWAY_INFO, sway_terminate);\n\t\twlr_log_init(WLR_INFO, handle_wlr_log);\n\t} else {\n\t\tsway_log_init(SWAY_ERROR, sway_terminate);\n\t\twlr_log_init(WLR_ERROR, handle_wlr_log);\n\t}\n\n\tsway_log(SWAY_INFO, \"Sway version \" SWAY_VERSION);\n\tsway_log(SWAY_INFO, \"wlroots version \" WLR_VERSION_STR);\n\tlog_kernel();\n\tlog_distro();\n\tlog_env();\n\n\tif (optind < argc) { // Behave as IPC client\n\t\tif (optind != 1) {\n\t\t\tsway_log(SWAY_ERROR,\n\t\t\t\t\t\"Detected both options and positional arguments. If you \"\n\t\t\t\t\t\"are trying to use the IPC client, options are not \"\n\t\t\t\t\t\"supported. Otherwise, check the provided arguments for \"\n\t\t\t\t\t\"issues. See `man 1 sway` or `sway -h` for usage. If you \"\n\t\t\t\t\t\"are trying to generate a debug log, use \"\n\t\t\t\t\t\"`sway -d 2>sway.log`.\");\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t\tchar *socket_path = getenv(\"SWAYSOCK\");\n\t\tif (!socket_path) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to retrieve socket path\");\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t\tchar *command = join_args(argv + optind, argc - optind);\n\t\trun_as_ipc_client(command, socket_path);\n\t\tfree(command);\n\t\treturn 0;\n\t}\n\n\tincrease_nofile_limit();\n\n\tsway_log(SWAY_INFO, \"Starting sway version \" SWAY_VERSION);\n\n\tif (!server_init(&server)) {\n\t\treturn 1;\n\t}\n\n\tinit_signals();\n\n\tif (server.linux_dmabuf_v1) {\n\t\twlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1);\n\t}\n\n\tif (validate) {\n\t\tbool valid = load_main_config(config_path, false, true);\n\t\tfree(config_path);\n\t\treturn valid ? 0 : 1;\n\t}\n\n\tipc_init(&server);\n\n\tsetenv(\"WAYLAND_DISPLAY\", server.socket, true);\n\tif (!load_main_config(config_path, false, false)) {\n\t\tsway_terminate(EXIT_FAILURE);\n\t\tgoto shutdown;\n\t}\n\n\tset_rr_scheduling();\n\n\tif (!server_start(&server)) {\n\t\tsway_terminate(EXIT_FAILURE);\n\t\tgoto shutdown;\n\t}\n\n\tconfig->active = true;\n\tforce_modeset();\n\tload_swaybars();\n\trun_deferred_commands();\n\trun_deferred_bindings();\n\ttransaction_commit_dirty();\n\n\tif (config->swaynag_config_errors.client != NULL) {\n\t\tswaynag_show(&config->swaynag_config_errors);\n\t}\n\n\tstruct swaynag_instance nag_gpu = (struct swaynag_instance){\n\t\t.args = \"--type error \"\n\t\t\t\"--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' \"\n\t\t\t\"--detailed-message\",\n\t\t.detailed = true,\n\t};\n\n\tif (unsupported_gpu_detected && !allow_unsupported_gpu) {\n\t\tswaynag_log(config->swaynag_command, &nag_gpu,\n\t\t\t\"To remove this message, launch sway with --unsupported-gpu \"\n\t\t\t\"or set the environment variable SWAY_UNSUPPORTED_GPU=true.\");\n\t\tswaynag_show(&nag_gpu);\n\t}\n\n\tserver_run(&server);\n\nshutdown:\n\tsway_log(SWAY_INFO, \"Shutting down sway\");\n\n\tserver_fini(&server);\n\troot_destroy(root);\n\troot = NULL;\n\n\tfree(config_path);\n\tfree_config(config);\n\n\tif (nag_gpu.client != NULL) {\n\t\twl_client_destroy(nag_gpu.client);\n\t}\n\n\tpango_cairo_font_map_set_default(NULL);\n\n\treturn exit_value;\n}\n"
  },
  {
    "path": "sway/meson.build",
    "content": "sway_sources = files(\n\t'commands.c',\n\t'config.c',\n\t'criteria.c',\n\t'decoration.c',\n\t'ipc-json.c',\n\t'ipc-server.c',\n\t'lock.c',\n\t'main.c',\n\t'realtime.c',\n\t'scene_descriptor.c',\n\t'server.c',\n\t'sway_text_node.c',\n\t'swaynag.c',\n\t'xdg_activation_v1.c',\n\t'xdg_decoration.c',\n\n\t'desktop/idle_inhibit_v1.c',\n\t'desktop/layer_shell.c',\n\t'desktop/output.c',\n\t'desktop/tearing.c',\n\t'desktop/transaction.c',\n\t'desktop/xdg_shell.c',\n\t'desktop/launcher.c',\n\n\t'input/input-manager.c',\n\t'input/cursor.c',\n\t'input/keyboard.c',\n\t'input/seat.c',\n\t'input/seatop_default.c',\n\t'input/seatop_down.c',\n\t'input/seatop_move_floating.c',\n\t'input/seatop_move_tiling.c',\n\t'input/seatop_resize_floating.c',\n\t'input/seatop_resize_tiling.c',\n\t'input/switch.c',\n\t'input/tablet.c',\n\t'input/text_input.c',\n\n\t'config/bar.c',\n\t'config/output.c',\n\t'config/seat.c',\n\t'config/input.c',\n\n\t'commands/allow_tearing.c',\n\t'commands/assign.c',\n\t'commands/bar.c',\n\t'commands/bind.c',\n\t'commands/border.c',\n\t'commands/client.c',\n\t'commands/create_output.c',\n\t'commands/default_border.c',\n\t'commands/default_floating_border.c',\n\t'commands/default_orientation.c',\n\t'commands/exit.c',\n\t'commands/exec.c',\n\t'commands/exec_always.c',\n\t'commands/floating.c',\n\t'commands/floating_minmax_size.c',\n\t'commands/floating_modifier.c',\n\t'commands/focus.c',\n\t'commands/focus_follows_mouse.c',\n\t'commands/focus_on_window_activation.c',\n\t'commands/focus_wrapping.c',\n\t'commands/font.c',\n\t'commands/for_window.c',\n\t'commands/force_display_urgency_hint.c',\n\t'commands/force_focus_wrapping.c',\n\t'commands/fullscreen.c',\n\t'commands/gaps.c',\n\t'commands/gesture.c',\n\t'commands/hide_edge_borders.c',\n\t'commands/inhibit_idle.c',\n\t'commands/kill.c',\n\t'commands/mark.c',\n\t'commands/max_render_time.c',\n\t'commands/opacity.c',\n\t'commands/include.c',\n\t'commands/input.c',\n\t'commands/layout.c',\n\t'commands/mode.c',\n\t'commands/mouse_warping.c',\n\t'commands/move.c',\n\t'commands/new_float.c',\n\t'commands/new_window.c',\n\t'commands/no_focus.c',\n\t'commands/nop.c',\n\t'commands/output.c',\n\t'commands/popup_during_fullscreen.c',\n\t'commands/primary_selection.c',\n\t'commands/reload.c',\n\t'commands/rename.c',\n\t'commands/resize.c',\n\t'commands/scratchpad.c',\n\t'commands/seat.c',\n\t'commands/seat/attach.c',\n\t'commands/seat/cursor.c',\n\t'commands/seat/fallback.c',\n\t'commands/seat/hide_cursor.c',\n\t'commands/seat/idle.c',\n\t'commands/seat/keyboard_grouping.c',\n\t'commands/seat/pointer_constraint.c',\n\t'commands/seat/shortcuts_inhibitor.c',\n\t'commands/seat/xcursor_theme.c',\n\t'commands/set.c',\n\t'commands/show_marks.c',\n\t'commands/shortcuts_inhibitor.c',\n\t'commands/smart_borders.c',\n\t'commands/smart_gaps.c',\n\t'commands/split.c',\n\t'commands/sticky.c',\n\t'commands/swaybg_command.c',\n\t'commands/swaynag_command.c',\n\t'commands/swap.c',\n\t'commands/tiling_drag.c',\n\t'commands/tiling_drag_threshold.c',\n\t'commands/title_align.c',\n\t'commands/title_format.c',\n\t'commands/titlebar_border_thickness.c',\n\t'commands/titlebar_padding.c',\n\t'commands/unmark.c',\n\t'commands/urgent.c',\n\t'commands/workspace.c',\n\t'commands/workspace_layout.c',\n\t'commands/ws_auto_back_and_forth.c',\n\t'commands/xwayland.c',\n\n\t'commands/bar/bind.c',\n\t'commands/bar/binding_mode_indicator.c',\n\t'commands/bar/colors.c',\n\t'commands/bar/font.c',\n\t'commands/bar/gaps.c',\n\t'commands/bar/height.c',\n\t'commands/bar/hidden_state.c',\n\t'commands/bar/icon_theme.c',\n\t'commands/bar/id.c',\n\t'commands/bar/mode.c',\n\t'commands/bar/modifier.c',\n\t'commands/bar/output.c',\n\t'commands/bar/pango_markup.c',\n\t'commands/bar/position.c',\n\t'commands/bar/separator_symbol.c',\n\t'commands/bar/status_command.c',\n\t'commands/bar/status_edge_padding.c',\n\t'commands/bar/status_padding.c',\n\t'commands/bar/strip_workspace_numbers.c',\n\t'commands/bar/strip_workspace_name.c',\n\t'commands/bar/swaybar_command.c',\n\t'commands/bar/tray_bind.c',\n\t'commands/bar/tray_output.c',\n\t'commands/bar/tray_padding.c',\n\t'commands/bar/workspace_buttons.c',\n\t'commands/bar/workspace_min_width.c',\n\t'commands/bar/wrap_scroll.c',\n\n\t'commands/input/accel_profile.c',\n\t'commands/input/calibration_matrix.c',\n\t'commands/input/click_method.c',\n\t'commands/input/clickfinger_button_map.c',\n\t'commands/input/drag.c',\n\t'commands/input/drag_lock.c',\n\t'commands/input/dwt.c',\n\t'commands/input/dwtp.c',\n\t'commands/input/events.c',\n\t'commands/input/left_handed.c',\n\t'commands/input/map_from_region.c',\n\t'commands/input/map_to_output.c',\n\t'commands/input/map_to_region.c',\n\t'commands/input/middle_emulation.c',\n\t'commands/input/natural_scroll.c',\n\t'commands/input/pointer_accel.c',\n        'commands/input/rotation_angle.c',\n\t'commands/input/repeat_delay.c',\n\t'commands/input/repeat_rate.c',\n\t'commands/input/scroll_button.c',\n\t'commands/input/scroll_button_lock.c',\n\t'commands/input/scroll_factor.c',\n\t'commands/input/scroll_method.c',\n\t'commands/input/tap.c',\n\t'commands/input/tap_button_map.c',\n\t'commands/input/tool_mode.c',\n\t'commands/input/xkb_capslock.c',\n\t'commands/input/xkb_file.c',\n\t'commands/input/xkb_layout.c',\n\t'commands/input/xkb_model.c',\n\t'commands/input/xkb_numlock.c',\n\t'commands/input/xkb_options.c',\n\t'commands/input/xkb_rules.c',\n\t'commands/input/xkb_switch_layout.c',\n\t'commands/input/xkb_variant.c',\n\n\t'commands/output/adaptive_sync.c',\n\t'commands/output/allow_tearing.c',\n\t'commands/output/background.c',\n\t'commands/output/disable.c',\n\t'commands/output/dpms.c',\n\t'commands/output/enable.c',\n\t'commands/output/hdr.c',\n\t'commands/output/max_render_time.c',\n\t'commands/output/mode.c',\n\t'commands/output/position.c',\n\t'commands/output/power.c',\n\t'commands/output/render_bit_depth.c',\n\t'commands/output/scale.c',\n\t'commands/output/scale_filter.c',\n\t'commands/output/subpixel.c',\n\t'commands/output/toggle.c',\n\t'commands/output/transform.c',\n\t'commands/output/unplug.c',\n\t'commands/output/color_profile.c',\n\n\t'tree/arrange.c',\n\t'tree/container.c',\n\t'tree/node.c',\n\t'tree/root.c',\n\t'tree/view.c',\n\t'tree/workspace.c',\n\t'tree/output.c',\n)\n\nsway_deps = [\n\tcairo,\n\tdrm,\n\tjsonc,\n\tlibevdev,\n\tlibinput,\n\tlibudev,\n\tmath,\n\tpango,\n\tpcre2,\n\tpixman,\n\tthreads,\n\twayland_server,\n\twlroots,\n\txkbcommon,\n\txcb,\n\txcb_icccm,\n]\n\nif wlroots_features['xwayland']\n\tsway_sources += 'desktop/xwayland.c'\nendif\n\nif wlroots_features['libinput_backend']\n\tsway_sources += 'input/libinput.c'\nendif\n\nexecutable(\n\t'sway',\n\tsway_sources + wl_protos_src,\n\tinclude_directories: [sway_inc],\n\tdependencies: sway_deps,\n\tlink_with: [lib_sway_common],\n\tinstall: true\n)\n"
  },
  {
    "path": "sway/realtime.c",
    "content": "#include <sys/resource.h>\n#include <sched.h>\n#include <unistd.h>\n#include <pthread.h>\n#include \"sway/server.h\"\n#include \"log.h\"\n\nstatic void child_fork_callback(void) {\n\tstruct sched_param param;\n\n\tparam.sched_priority = 0;\n\n\tint ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, &param);\n\tif (ret != 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to reset scheduler policy on fork\");\n\t}\n}\n\nvoid set_rr_scheduling(void) {\n\tint prio = sched_get_priority_min(SCHED_RR);\n\tint old_policy;\n\tint ret;\n\tstruct sched_param param;\n\n\tret = pthread_getschedparam(pthread_self(), &old_policy, &param);\n\tif (ret != 0) {\n\t\tsway_log(SWAY_DEBUG, \"Failed to get old scheduling priority\");\n\t\treturn;\n\t}\n\n\tparam.sched_priority = prio;\n\n\tret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);\n\tif (ret != 0) {\n\t\tsway_log(SWAY_INFO, \"Failed to set scheduling priority to %d\", prio);\n\t\treturn;\n\t}\n\n\tpthread_atfork(NULL, NULL, child_fork_callback);\n}\n"
  },
  {
    "path": "sway/scene_descriptor.c",
    "content": "#include <stdlib.h>\n#include <wlr/util/addon.h>\n#include \"log.h\"\n#include \"sway/scene_descriptor.h\"\n\nstruct scene_descriptor {\n\tvoid *data;\n\tstruct wlr_addon addon;\n};\n\nstatic const struct wlr_addon_interface addon_interface;\n\nstatic struct scene_descriptor *scene_node_get_descriptor(\n\t\tstruct wlr_scene_node *node, enum sway_scene_descriptor_type type) {\n\tstruct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface);\n\tif (!addon) {\n\t\treturn NULL;\n\t}\n\n\tstruct scene_descriptor *desc = wl_container_of(addon, desc, addon);\n\treturn desc;\n}\n\nstatic void descriptor_destroy(struct scene_descriptor *desc) {\n\twlr_addon_finish(&desc->addon);\n\tfree(desc);\n}\n\nvoid *scene_descriptor_try_get(struct wlr_scene_node *node,\n\t\tenum sway_scene_descriptor_type type) {\n\tstruct scene_descriptor *desc = scene_node_get_descriptor(node, type);\n\tif (!desc) {\n\t\treturn NULL;\n\t}\n\n\treturn desc->data;\n}\n\nvoid scene_descriptor_destroy(struct wlr_scene_node *node,\n\t\tenum sway_scene_descriptor_type type) {\n\tstruct scene_descriptor *desc = scene_node_get_descriptor(node, type);\n\tif (!desc) {\n\t\treturn;\n\t}\n\tdescriptor_destroy(desc);\n}\n\nstatic void addon_handle_destroy(struct wlr_addon *addon) {\n\tstruct scene_descriptor *desc = wl_container_of(addon, desc, addon);\n\tdescriptor_destroy(desc);\n}\n\nstatic const struct wlr_addon_interface addon_interface = {\n\t.name = \"sway_scene_descriptor\",\n\t.destroy = addon_handle_destroy,\n};\n\nbool scene_descriptor_assign(struct wlr_scene_node *node,\n\t\tenum sway_scene_descriptor_type type, void *data) {\n\tstruct scene_descriptor *desc = calloc(1, sizeof(*desc));\n\tif (!desc) {\n\t\tsway_log(SWAY_ERROR, \"Could not allocate a scene descriptor\");\n\t\treturn false;\n\t}\n\n\twlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface);\n\tdesc->data = data;\n\treturn true;\n}\n"
  },
  {
    "path": "sway/server.c",
    "content": "#include <assert.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <wayland-server-core.h>\n#include <wlr/backend.h>\n#include <wlr/backend/headless.h>\n#include <wlr/backend/multi.h>\n#include <wlr/config.h>\n#include <wlr/render/allocator.h>\n#include <wlr/render/wlr_renderer.h>\n#include <wlr/types/wlr_alpha_modifier_v1.h>\n#include <wlr/types/wlr_color_management_v1.h>\n#include <wlr/types/wlr_color_representation_v1.h>\n#include <wlr/types/wlr_compositor.h>\n#include <wlr/types/wlr_content_type_v1.h>\n#include <wlr/types/wlr_cursor_shape_v1.h>\n#include <wlr/types/wlr_data_control_v1.h>\n#include <wlr/types/wlr_ext_data_control_v1.h>\n#include <wlr/types/wlr_data_device.h>\n#include <wlr/types/wlr_export_dmabuf_v1.h>\n#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>\n#include <wlr/types/wlr_fixes.h>\n#include <wlr/types/wlr_foreign_toplevel_management_v1.h>\n#include <wlr/types/wlr_ext_image_capture_source_v1.h>\n#include <wlr/types/wlr_ext_image_copy_capture_v1.h>\n#include <wlr/types/wlr_fractional_scale_v1.h>\n#include <wlr/types/wlr_gamma_control_v1.h>\n#include <wlr/types/wlr_idle_notify_v1.h>\n#include <wlr/types/wlr_layer_shell_v1.h>\n#include <wlr/types/wlr_linux_dmabuf_v1.h>\n#include <wlr/types/wlr_linux_drm_syncobj_v1.h>\n#include <wlr/types/wlr_output_management_v1.h>\n#include <wlr/types/wlr_output_power_management_v1.h>\n#include <wlr/types/wlr_pointer_constraints_v1.h>\n#include <wlr/types/wlr_presentation_time.h>\n#include <wlr/types/wlr_primary_selection_v1.h>\n#include <wlr/types/wlr_relative_pointer_v1.h>\n#include <wlr/types/wlr_screencopy_v1.h>\n#include <wlr/types/wlr_security_context_v1.h>\n#include <wlr/types/wlr_server_decoration.h>\n#include <wlr/types/wlr_session_lock_v1.h>\n#include <wlr/types/wlr_single_pixel_buffer_v1.h>\n#include <wlr/types/wlr_subcompositor.h>\n#include <wlr/types/wlr_tablet_v2.h>\n#include <wlr/types/wlr_viewporter.h>\n#include <wlr/types/wlr_xcursor_manager.h>\n#include <wlr/types/wlr_xdg_activation_v1.h>\n#include <wlr/types/wlr_xdg_decoration_v1.h>\n#include <wlr/types/wlr_xdg_foreign_registry.h>\n#include <wlr/types/wlr_xdg_foreign_v1.h>\n#include <wlr/types/wlr_xdg_foreign_v2.h>\n#include <wlr/types/wlr_xdg_output_v1.h>\n#include <wlr/types/wlr_xdg_toplevel_tag_v1.h>\n#include <xf86drm.h>\n#include \"config.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"sway/config.h\"\n#include \"sway/desktop/idle_inhibit_v1.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/tree/root.h\"\n\n#if WLR_HAS_XWAYLAND\n#include <wlr/xwayland/shell.h>\n#include \"sway/xwayland.h\"\n#endif\n\n#if WLR_HAS_DRM_BACKEND\n#include <wlr/types/wlr_drm_lease_v1.h>\n#endif\n\n#define SWAY_XDG_SHELL_VERSION 5\n#define SWAY_LAYER_SHELL_VERSION 5\n#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1\n#define SWAY_PRESENTATION_VERSION 2\n\nbool unsupported_gpu_detected = false;\n\n#if WLR_HAS_DRM_BACKEND\nstatic void handle_drm_lease_request(struct wl_listener *listener, void *data) {\n\t/* We only offer non-desktop outputs, but in the future we might want to do\n\t * more logic here. */\n\n\tstruct wlr_drm_lease_request_v1 *req = data;\n\tstruct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);\n\tif (!lease) {\n\t\tsway_log(SWAY_ERROR, \"Failed to grant lease request\");\n\t\twlr_drm_lease_request_v1_reject(req);\n\t}\n}\n#endif\n\nstatic bool is_privileged(const struct wl_global *global) {\n#if WLR_HAS_DRM_BACKEND\n\tif (server.drm_lease_manager != NULL) {\n\t\tstruct wlr_drm_lease_device_v1 *drm_lease_dev;\n\t\twl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) {\n\t\t\tif (drm_lease_dev->global == global) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\treturn\n\t\tglobal == server.output_manager_v1->global ||\n\t\tglobal == server.output_power_manager_v1->global ||\n\t\tglobal == server.input_method->global ||\n\t\tglobal == server.foreign_toplevel_list->global ||\n\t\tglobal == server.foreign_toplevel_manager->global ||\n\t\tglobal == server.wlr_data_control_manager_v1->global ||\n\t\tglobal == server.ext_data_control_manager_v1->global ||\n\t\tglobal == server.screencopy_manager_v1->global ||\n\t\tglobal == server.ext_image_copy_capture_manager_v1->global ||\n\t\tglobal == server.export_dmabuf_manager_v1->global ||\n\t\tglobal == server.security_context_manager_v1->global ||\n\t\tglobal == server.gamma_control_manager_v1->global ||\n\t\tglobal == server.layer_shell->global ||\n\t\tglobal == server.session_lock.manager->global ||\n\t\tglobal == server.input->keyboard_shortcuts_inhibit->global ||\n\t\tglobal == server.input->virtual_keyboard->global ||\n\t\tglobal == server.input->virtual_pointer->global ||\n\t\tglobal == server.input->transient_seat_manager->global ||\n\t\tglobal == server.xdg_output_manager_v1->global;\n}\n\nstatic bool filter_global(const struct wl_client *client,\n\t\tconst struct wl_global *global, void *data) {\n#if WLR_HAS_XWAYLAND\n\tstruct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;\n\tif (xwayland && global == xwayland->shell_v1->global) {\n\t\treturn xwayland->server != NULL && client == xwayland->server->client;\n\t}\n#endif\n\n\t// Restrict usage of privileged protocols to unsandboxed clients\n\t// TODO: add a way for users to configure an allow-list\n\tconst struct wlr_security_context_v1_state *security_context =\n\t\twlr_security_context_manager_v1_lookup_client(\n\t\tserver.security_context_manager_v1, (struct wl_client *)client);\n\tif (is_privileged(global)) {\n\t\treturn security_context == NULL;\n\t}\n\n\treturn true;\n}\n\nstatic void detect_proprietary(struct wlr_backend *backend, void *data) {\n\tint drm_fd = wlr_backend_get_drm_fd(backend);\n\tif (drm_fd < 0) {\n\t\treturn;\n\t}\n\n\tdrmVersion *version = drmGetVersion(drm_fd);\n\tif (version == NULL) {\n\t\tsway_log(SWAY_ERROR, \"drmGetVersion() failed\");\n\t\treturn;\n\t}\n\n\tif (strcmp(version->name, \"nvidia-drm\") == 0) {\n\t\tunsupported_gpu_detected = true;\n\t\tsway_log(SWAY_ERROR, \"!!! Proprietary Nvidia drivers are in use !!!\");\n\t}\n\n\tif (strcmp(version->name, \"evdi\") == 0) {\n\t\tunsupported_gpu_detected = true;\n\t\tsway_log(SWAY_ERROR, \"!!! Proprietary DisplayLink drivers are in use !!!\");\n\t}\n\n\tdrmFreeVersion(version);\n}\n\nstatic void do_renderer_recreate(void *data) {\n\tstruct sway_server *server = data;\n\tserver->recreating_renderer = NULL;\n\n\tsway_log(SWAY_INFO, \"Re-creating renderer after GPU reset\");\n\tstruct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend);\n\tif (renderer == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Unable to create renderer\");\n\t\treturn;\n\t}\n\n\tstruct wlr_allocator *allocator =\n\t\twlr_allocator_autocreate(server->backend, renderer);\n\tif (allocator == NULL) {\n\t\tsway_log(SWAY_ERROR, \"Unable to create allocator\");\n\t\twlr_renderer_destroy(renderer);\n\t\treturn;\n\t}\n\n\tstruct wlr_renderer *old_renderer = server->renderer;\n\tstruct wlr_allocator *old_allocator = server->allocator;\n\tserver->renderer = renderer;\n\tserver->allocator = allocator;\n\n\twl_list_remove(&server->renderer_lost.link);\n\twl_signal_add(&server->renderer->events.lost, &server->renderer_lost);\n\n\twlr_compositor_set_renderer(server->compositor, renderer);\n\n\tstruct sway_output *output;\n\twl_list_for_each(output, &root->all_outputs, link) {\n\t\twlr_output_init_render(output->wlr_output,\n\t\t\tserver->allocator, server->renderer);\n\t}\n\n\twlr_allocator_destroy(old_allocator);\n\twlr_renderer_destroy(old_renderer);\n}\n\nstatic void handle_renderer_lost(struct wl_listener *listener, void *data) {\n\tstruct sway_server *server = wl_container_of(listener, server, renderer_lost);\n\n\tif (server->recreating_renderer != NULL) {\n\t\tsway_log(SWAY_DEBUG, \"Re-creation of renderer already scheduled\");\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_INFO, \"Scheduling re-creation of renderer after GPU reset\");\n\tserver->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server);\n}\n\nstatic void handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, void *data) {\n\tstruct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data;\n\tstruct sway_view *view = request->toplevel_handle->data;\n\n\tif (view->image_capture_source == NULL) {\n\t\tview->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node(\n\t\t\t&view->image_capture_scene->tree.node, server.wl_event_loop, server.allocator, server.renderer);\n\t\tif (view->image_capture_source == NULL) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\twlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(request, view->image_capture_source);\n}\n\nbool server_init(struct sway_server *server) {\n\tsway_log(SWAY_DEBUG, \"Initializing Wayland server\");\n\tserver->wl_display = wl_display_create();\n\tserver->wl_event_loop = wl_display_get_event_loop(server->wl_display);\n\n\twl_display_set_global_filter(server->wl_display, filter_global, NULL);\n\twl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024);\n\n\twlr_fixes_create(server->wl_display, 1);\n\troot = root_create(server->wl_display);\n\n\tserver->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session);\n\tif (!server->backend) {\n\t\tsway_log(SWAY_ERROR, \"Unable to create backend\");\n\t\treturn false;\n\t}\n\n\twlr_multi_for_each_backend(server->backend, detect_proprietary, NULL);\n\n\tserver->renderer = wlr_renderer_autocreate(server->backend);\n\tif (!server->renderer) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create renderer\");\n\t\treturn false;\n\t}\n\n\tserver->renderer_lost.notify = handle_renderer_lost;\n\twl_signal_add(&server->renderer->events.lost, &server->renderer_lost);\n\n\twlr_renderer_init_wl_shm(server->renderer, server->wl_display);\n\n\tif (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) {\n\t\tserver->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(\n\t\t\tserver->wl_display, 5, server->renderer);\n\t}\n\tif (wlr_renderer_get_drm_fd(server->renderer) >= 0 &&\n\t\t\tserver->renderer->features.timeline &&\n\t\t\tserver->backend->features.timeline) {\n\t\twlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1,\n\t\t\twlr_renderer_get_drm_fd(server->renderer));\n\t}\n\n\tserver->allocator = wlr_allocator_autocreate(server->backend,\n\t\tserver->renderer);\n\tif (!server->allocator) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create allocator\");\n\t\treturn false;\n\t}\n\n\tserver->compositor = wlr_compositor_create(server->wl_display, 6,\n\t\tserver->renderer);\n\n\twlr_subcompositor_create(server->wl_display);\n\n\tserver->data_device_manager =\n\t\twlr_data_device_manager_create(server->wl_display);\n\n\tserver->gamma_control_manager_v1 =\n\t\twlr_gamma_control_manager_v1_create(server->wl_display);\n\twlr_scene_set_gamma_control_manager_v1(root->root_scene,\n\t\tserver->gamma_control_manager_v1);\n\n\tserver->new_output.notify = handle_new_output;\n\twl_signal_add(&server->backend->events.new_output, &server->new_output);\n\n\tserver->xdg_output_manager_v1 =\n\t\twlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);\n\n\tserver->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);\n\tsway_idle_inhibit_manager_v1_init();\n\n\tserver->layer_shell = wlr_layer_shell_v1_create(server->wl_display,\n\t\tSWAY_LAYER_SHELL_VERSION);\n\twl_signal_add(&server->layer_shell->events.new_surface,\n\t\t&server->layer_shell_surface);\n\tserver->layer_shell_surface.notify = handle_layer_shell_surface;\n\n\tserver->xdg_shell = wlr_xdg_shell_create(server->wl_display,\n\t\tSWAY_XDG_SHELL_VERSION);\n\twl_signal_add(&server->xdg_shell->events.new_toplevel,\n\t\t&server->xdg_shell_toplevel);\n\tserver->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel;\n\n\tserver->tablet_v2 = wlr_tablet_v2_create(server->wl_display);\n\n\tserver->server_decoration_manager =\n\t\twlr_server_decoration_manager_create(server->wl_display);\n\twlr_server_decoration_manager_set_default_mode(\n\t\tserver->server_decoration_manager,\n\t\tWLR_SERVER_DECORATION_MANAGER_MODE_SERVER);\n\twl_signal_add(&server->server_decoration_manager->events.new_decoration,\n\t\t&server->server_decoration);\n\tserver->server_decoration.notify = handle_server_decoration;\n\twl_list_init(&server->decorations);\n\n\tserver->xdg_decoration_manager =\n\t\twlr_xdg_decoration_manager_v1_create(server->wl_display);\n\twl_signal_add(\n\t\t\t&server->xdg_decoration_manager->events.new_toplevel_decoration,\n\t\t\t&server->xdg_decoration);\n\tserver->xdg_decoration.notify = handle_xdg_decoration;\n\twl_list_init(&server->xdg_decorations);\n\n\tserver->relative_pointer_manager =\n\t\twlr_relative_pointer_manager_v1_create(server->wl_display);\n\n\tserver->pointer_constraints =\n\t\twlr_pointer_constraints_v1_create(server->wl_display);\n\tserver->pointer_constraint.notify = handle_pointer_constraint;\n\twl_signal_add(&server->pointer_constraints->events.new_constraint,\n\t\t&server->pointer_constraint);\n\n\twlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION);\n\twlr_alpha_modifier_v1_create(server->wl_display);\n\n\tserver->output_manager_v1 =\n\t\twlr_output_manager_v1_create(server->wl_display);\n\tserver->output_manager_apply.notify = handle_output_manager_apply;\n\twl_signal_add(&server->output_manager_v1->events.apply,\n\t\t&server->output_manager_apply);\n\tserver->output_manager_test.notify = handle_output_manager_test;\n\twl_signal_add(&server->output_manager_v1->events.test,\n\t\t&server->output_manager_test);\n\n\tserver->output_power_manager_v1 =\n\t\twlr_output_power_manager_v1_create(server->wl_display);\n\tserver->output_power_manager_set_mode.notify =\n\t\thandle_output_power_manager_set_mode;\n\twl_signal_add(&server->output_power_manager_v1->events.set_mode,\n\t\t&server->output_power_manager_set_mode);\n\tserver->input_method = wlr_input_method_manager_v2_create(server->wl_display);\n\tserver->text_input = wlr_text_input_manager_v3_create(server->wl_display);\n\tserver->foreign_toplevel_list =\n\t\twlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION);\n\tserver->foreign_toplevel_manager =\n\t\twlr_foreign_toplevel_manager_v1_create(server->wl_display);\n\n\tsway_session_lock_init();\n\n#if WLR_HAS_DRM_BACKEND\n\tserver->drm_lease_manager=\n\t\twlr_drm_lease_v1_manager_create(server->wl_display, server->backend);\n\tif (server->drm_lease_manager) {\n\t\tserver->drm_lease_request.notify = handle_drm_lease_request;\n\t\twl_signal_add(&server->drm_lease_manager->events.request,\n\t\t\t\t&server->drm_lease_request);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Failed to create wlr_drm_lease_device_v1\");\n\t\tsway_log(SWAY_INFO, \"VR will not be available\");\n\t}\n#endif\n\n\tserver->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display);\n\tserver->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display);\n\tserver->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1);\n\twlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1);\n\tserver->wlr_data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display);\n\tserver->ext_data_control_manager_v1 = wlr_ext_data_control_manager_v1_create(server->wl_display, 1);\n\tserver->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display);\n\twlr_viewporter_create(server->wl_display);\n\twlr_single_pixel_buffer_manager_v1_create(server->wl_display);\n\tserver->content_type_manager_v1 =\n\t\twlr_content_type_manager_v1_create(server->wl_display, 1);\n\twlr_fractional_scale_manager_v1_create(server->wl_display, 1);\n\n\tserver->ext_foreign_toplevel_image_capture_source_manager_v1 =\n\t\twlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(server->wl_display, 1);\n\tserver->new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request;\n\twl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request,\n\t\t&server->new_foreign_toplevel_capture_request);\n\n\tserver->tearing_control_v1 =\n\t\twlr_tearing_control_manager_v1_create(server->wl_display, 1);\n\tserver->tearing_control_new_object.notify = handle_new_tearing_hint;\n\twl_signal_add(&server->tearing_control_v1->events.new_object,\n\t\t&server->tearing_control_new_object);\n\twl_list_init(&server->tearing_controllers);\n\n\tstruct wlr_xdg_foreign_registry *foreign_registry =\n\t\twlr_xdg_foreign_registry_create(server->wl_display);\n\twlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);\n\twlr_xdg_foreign_v2_create(server->wl_display, foreign_registry);\n\n\tserver->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display);\n\tserver->xdg_activation_v1_request_activate.notify =\n\t\txdg_activation_v1_handle_request_activate;\n\twl_signal_add(&server->xdg_activation_v1->events.request_activate,\n\t\t&server->xdg_activation_v1_request_activate);\n\tserver->xdg_activation_v1_new_token.notify =\n\t\txdg_activation_v1_handle_new_token;\n\twl_signal_add(&server->xdg_activation_v1->events.new_token,\n\t\t&server->xdg_activation_v1_new_token);\n\n\tstruct wlr_xdg_toplevel_tag_manager_v1 *xdg_toplevel_tag_manager_v1 =\n\t\twlr_xdg_toplevel_tag_manager_v1_create(server->wl_display, 1);\n\tserver->xdg_toplevel_tag_manager_v1_set_tag.notify =\n\t\txdg_toplevel_tag_manager_v1_handle_set_tag;\n\twl_signal_add(&xdg_toplevel_tag_manager_v1->events.set_tag,\n\t\t&server->xdg_toplevel_tag_manager_v1_set_tag);\n\n\tstruct wlr_cursor_shape_manager_v1 *cursor_shape_manager =\n\t\twlr_cursor_shape_manager_v1_create(server->wl_display, 2);\n\tserver->request_set_cursor_shape.notify = handle_request_set_cursor_shape;\n\twl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape);\n\n\tif (server->renderer->features.input_color_transform) {\n\t\tconst enum wp_color_manager_v1_render_intent render_intents[] = {\n\t\t\tWP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL,\n\t\t};\n\t\tsize_t transfer_functions_len = 0;\n\t\tenum wp_color_manager_v1_transfer_function *transfer_functions =\n\t\t\twlr_color_manager_v1_transfer_function_list_from_renderer(server->renderer, &transfer_functions_len);\n\t\tsize_t primaries_len = 0;\n\t\tenum wp_color_manager_v1_primaries *primaries =\n\t\t\twlr_color_manager_v1_primaries_list_from_renderer(server->renderer, &primaries_len);\n\t\tstruct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create(\n\t\t\t\tserver->wl_display, 2, &(struct wlr_color_manager_v1_options){\n\t\t\t.features = {\n\t\t\t\t.parametric = true,\n\t\t\t\t.set_mastering_display_primaries = true,\n\t\t\t},\n\t\t\t.render_intents = render_intents,\n\t\t\t.render_intents_len = sizeof(render_intents) / sizeof(render_intents[0]),\n\t\t\t.transfer_functions = transfer_functions,\n\t\t\t.transfer_functions_len = transfer_functions_len,\n\t\t\t.primaries = primaries,\n\t\t\t.primaries_len = primaries_len,\n\t\t});\n\t\tfree(transfer_functions);\n\t\tfree(primaries);\n\t\twlr_scene_set_color_manager_v1(root->root_scene, cm);\n\t}\n\n\twlr_color_representation_manager_v1_create_with_renderer(\n\t\tserver->wl_display, 1, server->renderer);\n\n\twl_list_init(&server->pending_launcher_ctxs);\n\n\t// Avoid using \"wayland-0\" as display socket\n\tchar name_candidate[16];\n\tfor (unsigned int i = 1; i <= 32; ++i) {\n\t\tsnprintf(name_candidate, sizeof(name_candidate), \"wayland-%u\", i);\n\t\tif (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {\n\t\t\tserver->socket = strdup(name_candidate);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!server->socket) {\n\t\tsway_log(SWAY_ERROR, \"Unable to open wayland socket\");\n\t\twlr_backend_destroy(server->backend);\n\t\treturn false;\n\t}\n\n\tserver->headless_backend = wlr_headless_backend_create(server->wl_event_loop);\n\tif (!server->headless_backend) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create secondary headless backend\");\n\t\twlr_backend_destroy(server->backend);\n\t\treturn false;\n\t} else {\n\t\twlr_multi_backend_add(server->backend, server->headless_backend);\n\t}\n\n\tstruct wlr_output *wlr_output =\n\t\t\twlr_headless_add_output(server->headless_backend, 800, 600);\n\twlr_output_set_name(wlr_output, \"FALLBACK\");\n\troot->fallback_output = output_create(wlr_output);\n\n\t// This may have been set already via -Dtxn-timeout\n\tif (!server->txn_timeout_ms) {\n\t\tserver->txn_timeout_ms = 200;\n\t}\n\n\tserver->dirty_nodes = create_list();\n\n\tserver->input = input_manager_create(server);\n\tinput_manager_get_default_seat(); // create seat0\n\n\treturn true;\n}\n\nvoid server_fini(struct sway_server *server) {\n\t// remove listeners\n\twl_list_remove(&server->renderer_lost.link);\n\twl_list_remove(&server->new_output.link);\n\twl_list_remove(&server->layer_shell_surface.link);\n\twl_list_remove(&server->xdg_shell_toplevel.link);\n\twl_list_remove(&server->server_decoration.link);\n\twl_list_remove(&server->xdg_decoration.link);\n\twl_list_remove(&server->pointer_constraint.link);\n\twl_list_remove(&server->output_manager_apply.link);\n\twl_list_remove(&server->output_manager_test.link);\n\twl_list_remove(&server->output_power_manager_set_mode.link);\n#if WLR_HAS_DRM_BACKEND\n\tif (server->drm_lease_manager) {\n\t\twl_list_remove(&server->drm_lease_request.link);\n\t}\n#endif\n\twl_list_remove(&server->tearing_control_new_object.link);\n\twl_list_remove(&server->xdg_activation_v1_request_activate.link);\n\twl_list_remove(&server->xdg_activation_v1_new_token.link);\n\twl_list_remove(&server->xdg_toplevel_tag_manager_v1_set_tag.link);\n\twl_list_remove(&server->request_set_cursor_shape.link);\n\twl_list_remove(&server->new_foreign_toplevel_capture_request.link);\n\tinput_manager_finish(server->input);\n\n\t// TODO: free sway-specific resources\n#if WLR_HAS_XWAYLAND\n\tif (server->xwayland.wlr_xwayland != NULL) {\n\t\twl_list_remove(&server->xwayland_surface.link);\n\t\twl_list_remove(&server->xwayland_ready.link);\n\t\twlr_xwayland_destroy(server->xwayland.wlr_xwayland);\n\t}\n#endif\n\twl_display_destroy_clients(server->wl_display);\n\twlr_backend_destroy(server->backend);\n\twl_display_destroy(server->wl_display);\n\tlist_free(server->dirty_nodes);\n\tfree(server->socket);\n}\n\nbool server_start(struct sway_server *server) {\n#if WLR_HAS_XWAYLAND\n\tif (config->xwayland != XWAYLAND_MODE_DISABLED) {\n\t\tsway_log(SWAY_DEBUG, \"Initializing Xwayland (lazy=%d)\",\n\t\t\t\tconfig->xwayland == XWAYLAND_MODE_LAZY);\n\t\tserver->xwayland.wlr_xwayland =\n\t\t\twlr_xwayland_create(server->wl_display, server->compositor,\n\t\t\t\t\tconfig->xwayland == XWAYLAND_MODE_LAZY);\n\t\tif (!server->xwayland.wlr_xwayland) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to start Xwayland\");\n\t\t\tunsetenv(\"DISPLAY\");\n\t\t} else {\n\t\t\twl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface,\n\t\t\t\t&server->xwayland_surface);\n\t\t\tserver->xwayland_surface.notify = handle_xwayland_surface;\n\t\t\twl_signal_add(&server->xwayland.wlr_xwayland->events.ready,\n\t\t\t\t&server->xwayland_ready);\n\t\t\tserver->xwayland_ready.notify = handle_xwayland_ready;\n\n\t\t\tsetenv(\"DISPLAY\", server->xwayland.wlr_xwayland->display_name, true);\n\n\t\t\t/* xcursor configured by the default seat */\n\t\t}\n\t}\n#endif\n\n\tif (config->primary_selection) {\n\t\twlr_primary_selection_v1_device_manager_create(server->wl_display);\n\t}\n\n\tsway_log(SWAY_INFO, \"Starting backend on wayland display '%s'\",\n\t\t\tserver->socket);\n\tif (!wlr_backend_start(server->backend)) {\n\t\tsway_log(SWAY_ERROR, \"Failed to start backend\");\n\t\twlr_backend_destroy(server->backend);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid server_run(struct sway_server *server) {\n\tsway_log(SWAY_INFO, \"Running compositor on wayland display '%s'\",\n\t\t\tserver->socket);\n\twl_display_run(server->wl_display);\n}\n"
  },
  {
    "path": "sway/sway-bar.5.scd",
    "content": "sway-bar(5)\n\n# NAME\n\nsway-bar - bar configuration file and commands\n\n# DESCRIPTION\n\nSway allows configuring swaybar in the sway configuration file.\n\n# COMMANDS\n\nThe following commands may only be used in the configuration file.\n\n*id* <bar_id>\n\tSets the ID of the bar.\n\n*swaybar_command* <command>\n\tExecutes custom bar command. Default is _swaybar_.\n\nThe following commands may be used either in the configuration file or at\nruntime.\n\n*bindcode* [--release] <event-code> <command>\n\tExecutes _command_ when the mouse button has been pressed (or if _released_\n\tis given, when the button has been released). The buttons can be given as\n\tan event code, which can be obtaining from *libinput debug-events*. To\n\tdisable the default behavior for a button, use the command _nop_.\n\n*bindsym* [--release] button[1-9]|<event-name> <command>\n\tExecutes _command_ when the mouse button has been pressed (or if _released_\n\tis given, when the button has been released). The buttons can be given as a\n\tx11 button number or an event name, which can be obtained from *libinput\n\tdebug-events*. To disable the default behavior for a button, use the\n\tcommand _nop_.\n\n*binding_mode_indicator* yes|no\n\tEnable or disable binding mode indicator. Default is _yes_.\n\n*font* <font>\n\tSpecifies the font to be used in the bar. _font_ should be specified as a\n\tpango font description. For more information on pango font descriptions,\n\tsee https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description\n\n*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>\n\tSets the gaps from the edge of the screen for the bar. Gaps can either be\n\tset all at once, per direction, or per side. Note that only sides that\n\ttouch an edge of the screen can have gaps. For the side that does not\n\ttouch an edge of the screen, per-side outer gaps for workspaces may be of\n\tuse.\n\n*height* <height>\n\tSets the height of the bar. Default height (0) will match the font size.\n\n*hidden_state* hide|show [<bar-id>]\n\tSpecifies the behaviour of the bar when it is in _hide_ mode. When the\n\thidden state is _hide_, then it is normally hidden, and only unhidden by\n\tpressing the modifier key or in case of urgency hints. When the hidden\n\tstate is _show_, then it is permanently visible, drawn on top of the\n\tcurrently visible workspace. Default is _hide_.\n\n\tFor compatibility with i3, _bar hidden_state hide|show [<bar-id>]_ is\n\tsupported along with the sway only _bar <bar-id> hidden_state hide|show_\n\tsyntax. When using the i3 syntax, if _bar-id_ is omitted, the hidden_state\n\twill be changed for all bars. Attempting to use _bar <bar-id1>\n\thidden_state hide|show <bar-id2>_ will result in an error due to\n\tconflicting bar ids.\n\n*mode* dock|hide|invisible|overlay [<bar-id>]\n\tSpecifies the visibility of the bar. In _dock_ mode, it is permanently\n\tvisible at one edge of the screen. In _hide_ mode, it is hidden unless the\n\tmodifier key is pressed, though this behaviour depends on the hidden state.\n\tIn _invisible_ mode, it is permanently hidden. In _overlay_ mode, it is\n\tpermanently visible on top of other windows. (In _overlay_ mode the bar is\n\ttransparent to input events.) Default is _dock_.\n\n\tFor compatibility with i3, _bar mode <mode> [<bar-id>]_ syntax is supported\n\talong with the sway only _bar <bar-id> mode <mode>_ syntax. When using the\n\ti3 syntax, if _bar-id_ is omitted, the mode will be changed for all bars.\n\tAttempting to use _bar <bar-id1> mode <mode> <bar-id2>_ will result in an\n\terror due to conflicting bar ids.\n\n*modifier* <Modifier>|none\n\tSpecifies the modifier key that shows a hidden bar. Default is _Mod4_.\n\n*output* <output>|\\*\n\tRestrict the bar to a certain output, can be specified multiple times. If\n\tthe output command is omitted, the bar will be displayed on all outputs. _\\*_\n\tcan be given at any point to reset it back to all outputs.\n\n*pango_markup* enabled|disabled\n\tEnables or disables pango markup for status lines. This has no effect on\n\tstatus lines using the i3bar JSON protocol.\n\n*position* top|bottom\n\tSets position of the bar. Default is _bottom_.\n\n*separator_symbol* <symbol>\n\tSpecifies the separator symbol to separate blocks on the bar.\n\n*status_command* <status command>\n\tExecutes the bar _status command_ with _sh -c_. Each line of text printed\n\tto stdout from this command will be displayed in the status area of the\n\tbar. You may also use swaybar's JSON status line protocol. See\n\t*swaybar-protocol*(7) for more information on the protocol\n\n\tIf running this command via IPC, you can disable a running status command by\n\tsetting the command to a single dash: _swaybar bar bar-0 status\\_command -_\n\n*status_edge_padding* <padding>\n\tSets the padding that is used when the status line is at the right edge of\n\tthe bar. This value will be multiplied by the output scale. The default is\n\t_3_.\n\n*status_padding* <padding>\n\tSets the vertical padding that is used for the status line. The default is\n\t_1_. If _padding_ is _0_, blocks will be able to take up the full height of\n\tthe bar. This value will be multiplied by the output scale.\n\n*strip_workspace_name* yes|no\n\tIf set to _yes_, then workspace names will be omitted from the workspace\n\tbutton and only the custom number will be shown. Default is _no_.\n\n*strip_workspace_numbers* yes|no\n\tIf set to _yes_, then workspace numbers will be omitted from the workspace\n\tbutton and only the custom name will be shown. Default is _no_.\n\n*unbindcode* [--release] <event-code>\n\tRemoves the binding with the given <event-code>.\n\n*unbindsym* [--release] button[1-9]|<event-name>\n\tRemoves the binding with the given <button> or <event-name>.\n\n*wrap_scroll* yes|no\n\tEnables or disables wrapping when scrolling through workspaces with the\n\tscroll wheel. Default is _no_.\n\n*workspace_buttons* yes|no\n\tEnables or disables workspace buttons on the bar. Default is _yes_.\n\n*workspace_min_width* <px> [px]\n\tSpecifies the minimum width for the workspace buttons on the bar. Default is _0_.\n\n\tThis setting also applies to the current binding mode indicator.\n\nThe following commands may only be used at runtime.\n\n*mode* toggle [<bar_id>]\n\tToggles the current mode between _hide_ and _dock_. Any other mode\n\tis treated as _hide_.\n\n## TRAY\n\nSwaybar provides a system tray where third-party applications may place icons.\nThe following commands configure the tray.\n\n*tray_bindcode* <event-code>\nContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollUp|nop\n\tExecutes the action when the mouse button has been pressed. The buttons can\n\tbe given as an event code, which can be obtained from *libinput debug-events*.\n\tTo disable the default behavior for a button, use the command _nop_.\n\n*tray_bindsym* button[1-9]|<event-name>\nContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollUp|nop\n\tExecutes the action when the mouse button has been pressed. The buttons can\n\tbe given as a x11 button number or an event name, which can be obtained\n\tfrom *libinput debug-events*. Use the command _nop_ to disable the default\n\taction (Activate for button1, ContextMenu for button2 and SecondaryActivate\n\tfor button3).\n\n*tray_padding* <px> [px]\n\tSets the pixel padding of the system tray. This padding will surround the\n\ttray on all sides and between each item. The default value for _px_ is 2.\n\n*tray_output* none|<output>|\\*\n\tRestrict the tray to a certain output, can be specified multiple times. If\n\tomitted, the tray will be displayed on all outputs. Unlike i3bar, swaybar\n\tcan show icons on any number of bars and outputs without races. _\\*_ can be\n\tgiven at any point to reset it to display on all outputs.\n\n*icon_theme* <name>\n\tSets the icon theme that sway will look for item icons in. This option has\n\tno default value, because sway will always default to the fallback theme,\n\thicolor.\n\n## COLORS\n\nColors are defined within a _colors { }_ block inside a _bar { }_ block. Colors\nmust be defined in hex: _#RRGGBB_ or _#RRGGBBAA_.\n\n*background* <color>\n\tBackground color of the bar.\n\n*statusline* <color>\n\tText color to be used for the statusline.\n\n*separator* <color>\n\tText color to be used for the separator.\n\n*focused_background* <color>\n\tBackground color of the bar on the currently focused monitor output. If not\n\tused, the color will be taken from _background_.\n\n*focused_statusline* <color>\n\tText color to be used for the statusline on the currently focused monitor\n\toutput. If not used, the color will be taken from _statusline_.\n\n*focused_separator* <color>\n\tText color to be used for the separator on the currently focused monitor\n\toutput. If not used, the color will be taken from _separator_.\n\n*focused_workspace* <border> <background> <text>\n\tBorder, background and text color for a workspace button when the workspace\n\thas focus.\n\n*active_workspace* <border> <background> <text>\n\tBorder, background and text color for a workspace button when the workspace\n\tis active (visible) on some output, but the focus is on another one. You\n\tcan only tell this apart from the focused workspace when you are using\n\tmultiple monitors.\n\n*inactive_workspace* <border> <background> <text>\n\tBorder, background and text color for a workspace button when the workspace\n\tdoes not have focus and is not active (visible) on any output. This will be\n\tthe case for most workspaces.\n\n*urgent_workspace* <border> <background> <text>\n\tBorder, background and text color for a workspace button when the workspace\n\tcontains a window with the urgency hint set.\n\n*binding_mode* <border> <background> <text>\n\tBorder, background and text color for the binding mode indicator. If not used,\n\tthe colors will be taken from _urgent_workspace_.\n\n# SEE ALSO\n\n*sway*(5) *swaybar-protocol*(7)\n"
  },
  {
    "path": "sway/sway-input.5.scd",
    "content": "sway-input(5)\n\n# NAME\n\nsway-input - input configuration file and commands\n\n# DESCRIPTION\n\nSway allows for configuration of devices within the sway configuration file.\nTo obtain a list of available device identifiers, run *swaymsg -t get_inputs*.\nSettings can also be applied to all input devices by using the wildcard, _\\*_,\nin place of _\\<identifier\\>_ in the commands below. In addition, the settings\ncan be applied to a type of device, by using _type:\\<input_type\\>_ in place\nof _\\<identifier\\>_.\n\nIn the configuration file, settings with a more specific selector take\nprecedence over more general ones: _\\<identifier\\>_ \\> _type:\\<input_type\\>_ \\>\n_\\*_.  When executing input commands, however, the settings are applied to all\nmatching input devices!  This means that _type:\\<input_type\\>_ can override\npreviously set _\\<identifier\\>_ settings, even though in a configuration file\nthey would take precedence.  Similarly _\\*_ can override both _\\<identifier\\>_\nand _type:\\<input_type\\>_ settings, if applied later.\n\nTip: If the configuration settings do not appear to be taking effect, you could\ntry using _\\*_ instead of _\\<identifier\\>_. If it works with the wildcard, try\nusing a different identifier from *swaymsg -t get_inputs* until you find the\ncorrect input device.\n\nCurrent available input types are:\n\n- touchpad\n- pointer\n- keyboard\n- touch\n- tablet_tool\n- tablet_pad\n- switch\n\nNote: The type configurations are applied as the devices appear and get applied\non top of the existing device configurations.\n\n# INPUT COMMANDS\n\n## KEYBOARD CONFIGURATION\n\n*input* <identifier> repeat_delay <milliseconds>\n\tSets the amount of time a key must be held before it starts repeating.\n\n*input* <identifier> repeat_rate <characters per second>\n\tSets the frequency of key repeats once the repeat_delay has passed.\n\nFor more information on these xkb configuration options, see\n*xkeyboard-config*(7).\n\n*input* <identifier> xkb_file <file_name>\n\tSets all xkb configurations from a complete .xkb file. This file can be\n\tdumped from _xkbcomp $DISPLAY keymap.xkb_. This setting overrides\n\txkb_layout, xkb_model, xkb_options, xkb_rules, and xkb_variant settings.\n\n*input* <identifier> xkb_layout <layout_name>\n\tSets the layout of the keyboard like _us_ or _de_.\n\n\tMultiple layouts can be specified by separating them with commas.\n\n*input* <identifier> xkb_model <model_name>\n\tSets the model of the keyboard. This has an influence for some extra keys\n\tyour keyboard might have.\n\n*input* <identifier> xkb_options <options>\n\tSets extra xkb configuration options for the keyboard.\n\n\tMultiple options can be specified by separating them with commas.\n\n*input* <identifier> xkb_rules <rules>\n\tSets files of rules to be used for keyboard mapping composition.\n\n*input* <identifier> xkb_switch_layout <index>|next|prev\n\tChanges the active keyboard layout to <index> counting from zero or to\n\tnext or previous layout on the list. If there is no next or previous\n\tlayout, this command hops to the other end of the list.\n\n\tThis can be used when multiple layouts are configured with *xkb_layout*.\n\tA list of layouts you can switch between can be obtained with\n\t*swaymsg -t get_inputs*.\n\n*input* <identifier> xkb_variant <variant>\n\tSets the variant of the keyboard like _dvorak_ or _colemak_.\n\nThe following commands may only be used in the configuration file.\n\n*input* <identifier> xkb_capslock enabled|disabled\n\tInitially enables or disables CapsLock on startup, the default is disabled.\n\n*input* <identifier> xkb_numlock enabled|disabled\n\tInitially enables or disables NumLock on startup, the default is disabled.\n\n## TABLET CONFIGURATION\n\n*input* <identifier> tool_mode <tool> <absolute|relative>\n\tSets whether movement of a tablet tool should be treated as absolute or\n\trelative; the default is absolute.\n\n\tValid values for _\\<tool\\>_ are currently \"pen\", \"eraser\", \"brush\",\n\t\"pencil\", \"airbrush\", and the wildcard _\\*_, which matches all tools.\n\n\tMouse and lens tools ignore this setting and are always treated as relative.\n\n## MAPPING CONFIGURATION\n\n*input* <identifier> map_to_output <identifier>\n\tMaps inputs from this device to the specified output. Only meaningful if the\n\tdevice is a pointer, touch, or drawing tablet device.\n\n\tThe wildcard _\\*_ can be used to map the input device to the whole desktop\n\tlayout.\n\n*input* <identifier> map_to_region <X> <Y> <width> <height>\n\tMaps inputs from this device to the specified region of the global output\n\tlayout. Only meaningful if the device is a pointer, touch, or drawing tablet\n\tdevice.\n\n*input* <identifier> map_from_region <X1xY1> <X2xY2>\n\tIgnores inputs from this device that do not occur within the specified\n\tregion. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the\n\tfull available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all\n\tdevices support millimeters. Only meaningful if the device is not a\n\tkeyboard and provides events in absolute terms (such as a drawing tablet\n\tor touch screen - most pointers provide events relative to the previous\n\tframe).\n\n\tCommonly used to maintain the aspect ratio of the input device and screen.\n\tCropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as\n\tthe argument.\n\n## LIBINPUT CONFIGURATION\n\n*input* <identifier> accel_profile adaptive|flat\n\tSets the pointer acceleration profile for the specified input device.\n\n*input* <identifier> calibration_matrix <6 space-separated floating point values>\n\tSets the calibration matrix.\n\n*input* <identifier> click_method none|button_areas|clickfinger\n\tChanges the click method for the specified device.\n\n*input* <identifier> clickfinger_button_map lrm|lmr\n\tSpecifies which button mapping to use for clickfinger. _lrm_ treats 1 finger as\n\tleft click, 2 fingers as right click, and 3 fingers as middle click. _lmr_\n\ttreats 1 finger as left click, 2 fingers as middle click, and 3 fingers as\n\tright click.\n\n*input* <identifier> drag enabled|disabled\n\tEnables or disables tap-and-drag for specified input device.\n\n*input* <identifier> drag_lock enabled|disabled|enabled_sticky\n\tEnables or disables drag lock for specified input device. The default is\n\t_enabled_sticky_.\n\n*input* <identifier> dwt enabled|disabled\n\tEnables or disables disable-while-typing for the specified input device.\n\n*input* <identifier> dwtp enabled|disabled\n\tEnables or disables disable-while-trackpointing for the specified input\n\tdevice.\n\n*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>]\n\tEnables or disables send_events for specified input device. Disabling\n\tsend_events disables the input device.\n\n\tThe _toggle_ option cannot be used in the config. If no toggle modes are\n\tlisted, all supported modes for the device will be toggled through in the\n\torder: enabled,\tdisabled_on_external_mouse, disabled, (loop back). If\n\ttoggle modes are listed, they will be cycled through, defaulting to the\n\tfirst mode listed if the current mode is not in the list. They will also\n\tnot be checked to see if they are supported for the device and may fail.\n\n*input* <identifier> left_handed enabled|disabled\n\tEnables or disables left handed mode for specified input device.\n\n*input* <identifier> middle_emulation enabled|disabled\n\tEnables or disables middle click emulation.\n\n*input* <identifier> natural_scroll enabled|disabled\n\tEnables or disables natural (inverted) scrolling for the specified input\n\tdevice.\n\n*input* <identifier> pointer_accel [<-1|1>]\n\tChanges the pointer acceleration for the specified input device.\n\n*input* <identifier> rotation_angle <angle>\n\tSets the rotation angle of the device to the given clockwise angle in\n\tdegrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive).\n\n*input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name>\n\tSets the button used for scroll_method on_button_down. The button can\n\tbe given as an event name or code, which can be obtained from *libinput\n\tdebug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to\n\t_disable_, it disables the scroll_method on_button_down.\n\n*input* <identifier> scroll_button_lock enabled|disabled\n\tEnables or disables scroll button lock for specified input device.\n\n*input* <identifier> scroll_factor <floating point value>\n\tChanges the scroll factor for the specified input device. Scroll speed will\n\tbe scaled by the given value, which must be non-negative.\n\n*input* <identifier> scroll_method none|two_finger|edge|on_button_down\n\tChanges the scroll method for the specified input device.\n\n*input* <identifier> tap enabled|disabled\n\tEnables or disables tap for specified input device.\n\n*input* <identifier> tap_button_map lrm|lmr\n\tSpecifies which button mapping to use for tapping. _lrm_ treats 1 finger as\n\tleft click, 2 fingers as right click, and 3 fingers as middle click. _lmr_\n\ttreats 1 finger as left click, 2 fingers as middle click, and 3 fingers as\n\tright click.\n\n## SEAT CONFIGURATION\n\nConfigure options for multiseat mode.\n\nA *seat* is a collection of input devices that act independently of each other.\nSeats are identified by name and the default seat is _seat0_ if no seats are\nconfigured. While sway is running, _-_ (hyphen) can be used as an alias for the\ncurrent seat. Each seat has an independent keyboard focus and a separate cursor\nthat is controlled by the pointer devices of the seat. This is useful for\nmultiple people using the desktop at the same time with their own devices (each\nsitting in their own \"seat\"). The wildcard character, _\\*_, can also be used in\nplace of _\\<identifier\\>_ to change settings for all seats.\n\nTip: If the configuration settings do not appear to be taking effect, you could\ntry using _\\*_ instead of _\\<identifier\\>_. If it works with the wildcard, try\nusing a different identifier from *swaymsg -t get_seats* until you find the\ncorrect seat.\n\n*seat* <name> attach <input_identifier>\n\tAttach an input device to this seat by its input identifier. A special\n\tvalue of \"\\*\" will attach all devices to the seat.\n\n*seat* <seat> cursor move|set <x> <y>\n\tMove specified seat's cursor relative to current position or wrap to\n\tabsolute coordinates (with respect to the global coordinate space).\n\tSpecifying either value as 0 will not update that coordinate.\n\n\tDeprecated: use the virtual-pointer Wayland protocol instead.\n\n*seat* <seat> cursor press|release button[1-9]|<event-name-or-code>\n\tSimulate pressing (or releasing) the specified mouse button on the\n\tspecified seat. The button can either be provided as a button event name or\n\tevent code, which can be obtained from *libinput debug-events*, or as an x11\n\tmouse button (button[1-9]). If using button[4-7], which map to axes, an axis\n\tevent will be simulated, however _press_ and _release_ will be ignored and\n\tboth will occur.\n\n\tDeprecated: use the virtual-pointer Wayland protocol instead.\n\n*seat* <name> fallback true|false\n\tSet this seat as the fallback seat. A fallback seat will attach any device\n\tnot explicitly attached to another seat (similar to a \"default\" seat).\n\n*seat* <name> hide_cursor <timeout>|when-typing [enable|disable]\n\tHides the cursor image after the specified event occurred.\n\n\tIf _timeout_ is specified, then the cursor will be hidden after _timeout_\n\t(in milliseconds) has elapsed with no activity on the cursor. A timeout of 0\n\t(default) disables hiding the cursor. The minimal timeout is 100 and any\n\tvalue less than that (aside from 0), will be increased to 100.\n\n\tIf _when-typing_ is enabled, then the cursor will be hidden whenever a key\n\tis pressed.\n\n\tBe aware that this setting can interfere with input handling in games and\n\tcertain types of software (Gimp, Blender etc) that rely on simultaneous\n\tinput from mouse and keyboard.\n\n*seat* <name> idle_inhibit <sources...>\n\tSets the set of input event sources which can prevent the seat from\n\tbecoming idle, as a space separated list of source names. Valid names are\n\t\"keyboard\", \"pointer\", \"touchpad\", \"touch\", \"tablet_pad\", \"tablet_tool\",\n\tand \"switch\". The default behavior is to prevent idle on any event.\n\n*seat* <name> keyboard_grouping none|smart\n\tSet how the keyboards in the seat are grouped together. Currently, there\n\tare two options. _none_ will disable all keyboard grouping. This will make\n\tit so each keyboard device has its own isolated state. _smart_ will\n\tgroup the keyboards in the seat by their keymap and repeat info. This is\n\tuseful for when the keyboard appears as multiple separate input devices.\n\tIn this mode, the effective layout is synced between the keyboards in the\n\tgroup. The default is _smart_. To restore the behavior of older versions\n\tof sway, use _none_.\n\n*seat* <name> pointer_constraint enable|disable|escape\n\tEnables or disables the ability for clients to capture the cursor (enabled\n\tby default) for the seat. This is primarily useful for video games. The\n\t\"escape\" command can be used at runtime to escape from a captured client.\n\n*seat* <name> shortcuts_inhibitor enable|disable|activate|deactivate|toggle\n\tEnables or disables the ability of clients to inhibit keyboard\n\tshortcuts for the seat. This is primarily useful for virtualization and\n\tremote desktop software. Subcommands _enable_ and _disable_ affect\n\twhether future inhibitors are honoured by default, i.e. activated\n\tautomatically, the default being _enable_. When used at runtime,\n\t_disable_ also disables any currently active inhibitors. _activate_,\n\t_deactivate_ and _toggle_ are only usable at runtime and change the\n\tstate of a potentially existing inhibitor on the currently focused\n\twindow. This can be used with the current seat alias (_-_) to affect\n\tonly the currently focused window of the current seat. Subcommand\n\t_deactivate_ is particularly useful in an _--inhibited_ *bindsym* to\n\tescape a state where shortcuts are inhibited and the client becomes\n\tuncooperative. It is worth noting that whether disabled or deactivated\n\tinhibitors are removed is entirely up to the client. Depending on the\n\tclient it may therefore be possible to (re-)activate them later. Any\n\tvisual indication that an inhibitor is present is currently left to the\n\tclient as well.\n\n*seat* <name> xcursor_theme <theme> [<size>]\n\tOverride the system default XCursor theme. The default seat's\n\t(_seat0_) theme is also used as the default cursor theme in\n\tXWayland, and exported through the _XCURSOR_THEME_ and\n\t_XCURSOR_SIZE_ environment variables.\n\n# SEE ALSO\n\n*sway*(5) *sway-output*(5) *xkeyboard-config*(7)\n"
  },
  {
    "path": "sway/sway-ipc.7.scd",
    "content": "sway-ipc(7)\n\n# NAME\n\nsway-ipc - IPC protocol for sway\n\n# DESCRIPTION\n\nThis details the interprocess communication (IPC) protocol for *sway*(1). This\nIPC protocol can be used to control or obtain information from a sway process.\n\nThe IPC protocol uses a UNIX socket as the method of communication. The path\nto the socket is stored in the environment variable _SWAYSOCK_ and, for\nbackwards compatibility with i3, _I3SOCK_. You can also retrieve the socket\npath by calling _sway --get-socketpath_.\n\n# MESSAGE AND REPLY FORMAT\n\nThe format for messages and replies is:\n\t<magic-string> <payload-length> <payload-type> <payload>\nWhere++\n\t<magic-string> is _i3-ipc_, for compatibility with i3++\n\t<payload-length> is a 32-bit integer in native byte order++\n\t<payload-type> is a 32-bit integer in native byte order\n\nFor example, sending the _exit_ command would look like the following hexdump:\n```\n00000000 | 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex|\n00000010 | 69 74                                           |it              |\n```\n\nThe payload for replies will be a valid serialized JSON data structure.\n\n# MESSAGES AND REPLIES\n\nThe following message types and their corresponding reply types are currently\nsupported. *For all replies, any properties not listed are subject to removal.*\n\n[- *TYPE NUMBER*\n:- *MESSAGE NAME*\n:- *PURPOSE*\n|- 0\n:  RUN_COMMAND\n:[ Runs the payload as sway commands\n|- 1\n:  GET_WORKSPACES\n:  Get the list of current workspaces\n|- 2\n:  SUBSCRIBE\n:  Subscribe the IPC connection to the events listed in the payload\n|- 3\n:  GET_OUTPUTS\n:  Get the list of current outputs\n|- 4\n:  GET_TREE\n:  Get the node layout tree\n|- 5\n:  GET_MARKS\n:  Get the names of all the marks currently set\n|- 6\n:  GET_BAR_CONFIG\n:  Get the specified bar config or a list of bar config names\n|- 7\n:  GET_VERSION\n:  Get the version of sway that owns the IPC socket\n|- 8\n:  GET_BINDING_MODES\n:  Get the list of binding mode names\n|- 9\n:  GET_CONFIG\n:  Returns the config that was last loaded\n|- 10\n:  SEND_TICK\n:  Sends a tick event with the specified payload\n|- 11\n:  SYNC\n:  Replies failure object for i3 compatibility\n|- 12\n:  GET_BINDING_STATE\n:  Request the current binding state, e.g. the currently active binding mode name.\n|- 100\n:  GET_INPUTS\n:  Get the list of input devices\n|- 101\n:  GET_SEATS\n:  Get the list of seats\n\n## 0. RUN_COMMAND\n\n*MESSAGE*++\nParses and runs the payload as sway commands\n\n*REPLY*++\nAn array of objects corresponding to each command that was parsed. Each object\nhas the property _success_, which is a boolean indicating whether the command\nwas successful. The object may also contain the properties _error_ and _parse\\_error_.\nThe _error_ property is a human readable error message while _parse\\_error_ is a\nboolean indicating whether the reason the command failed was because the command\nwas unknown or not able to be parsed.\n\n*Example Reply:*\n```\n[\n\t{\n\t\t\"success\": true\n\t},\n\t{\n\t\t\"success\": false,\n\t\t\"parse_error\": true,\n\t\t\"error\": \"Invalid/unknown command\"\n\t}\n]\n```\n\n## 1. GET_WORKSPACES\n\n*MESSAGE*++\nRetrieves the list of workspaces.\n\n*REPLY*++\nThe reply is an array of objects corresponding to each workspace. Each object\nhas the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- num\n:  integer\n:[ The workspace number or -1 for workspaces that do not start with a number\n|- name\n:  string\n:  The name of the workspace\n|- visible\n:  boolean\n:  Whether the workspace is currently visible on any output\n|- focused\n:  boolean\n:  Whether the workspace is currently focused by the default seat (_seat0_)\n|- urgent\n:  boolean\n:  Whether a window on the workspace has the urgent flag set\n|- rect\n:  object\n:  The bounds of the workspace. It consists of _x_, _y_, _width_, and _height_\n|- output\n:  string\n:  The name of the output that the workspace is on\n\n\n*Example Reply:*\n```\n[\n\t{\n\t\t\"num\": 1,\n\t\t\"name\": \"1\",\n\t\t\"visible\": true,\n\t\t\"focused\": true,\n\t\t\"rect\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 23,\n\t\t\t\"width\": 1920,\n\t\t\t\"height\": 1057\n\t\t},\n\t\t\"output\": \"eDP-1\"\n\t},\n]\n```\n\n## 2. SUBSCRIBE\n\n*MESSAGE*++\nSubscribe this IPC connection to the event types specified in the message\npayload. The payload should be a valid JSON array of events. See the _EVENTS_\nsection for the list of supported events.\n\n*REPLY*++\nA single object that contains the property _success_, which is a boolean value\nindicating whether the subscription was successful or not.\n\n*Example Reply:*\n```\n{\n\t\"success\": true\n}\n```\n\n## 3. GET_OUTPUTS\n\n*MESSAGE*++\nRetrieve the list of outputs\n\n*REPLY*++\nAn array of objects corresponding to each output. Each object has the\nfollowing properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- name\n:  string\n:[ The name of the output. On DRM, this is the connector\n|- make\n:  string\n:  The make of the output\n|- model\n:  string\n:  The model of the output\n|- serial\n:  string\n:  The output's serial number as a hexadecimal string\n|- active\n:  boolean\n:  Whether this output is active/enabled\n|- dpms\n:  boolean\n:  (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS)\n|- power\n:  boolean\n:  Whether this output is on/off\n|- primary\n:  boolean\n:  For i3 compatibility, this will be false. It does not make sense in Wayland\n|- scale\n:  float\n:  The scale currently in use on the output or _-1_ for disabled outputs\n|- subpixel_hinting\n:  string\n:  The subpixel hinting current in use on the output. This can be _rgb_, _bgr_, _vrgb_, _vbgr_, or _none_\n|- transform\n:  string\n:  The transform currently in use for the output. This can be _normal_, _90_,\n   _180_, _270_, _flipped-90_, _flipped-180_, or _flipped-270_\n|- current_workspace\n:  string\n:  The workspace currently visible on the output or _null_ for disabled outputs\n|- modes\n:  array\n:  An array of supported mode objects. Each object contains _width_, _height_,\n   and _refresh_\n|- current_mode\n:  object\n:  An object representing the current mode containing _width_, _height_, and\n   _refresh_\n|- rect\n:  object\n:  The bounds for the output consisting of _x_, _y_, _width_, and _height_\n|- hdr\n:  boolean\n:  Whether HDR is enabled\n\n\n*Example Reply:*\n```\n[\n\t{\n\t\t\"name\": \"HDMI-A-2\",\n\t\t\"make\": \"Unknown\",\n\t\t\"model\": \"NS-19E310A13\",\n\t\t\"serial\": \"0x00000001\",\n\t\t\"active\": true,\n\t\t\"primary\": false,\n\t\t\"scale\": 1.0,\n\t\t\"subpixel_hinting\": \"rgb\",\n\t\t\"transform\": \"normal\",\n\t\t\"current_workspace\": \"1\",\n\t\t\"modes\": [\n\t\t\t{\n\t\t\t\t\"width\": 640,\n\t\t\t\t\"height\": 480,\n\t\t\t\t\"refresh\": 59940\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"width\": 800,\n\t\t\t\t\"height\": 600,\n\t\t\t\t\"refresh\": 60317\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"width\": 1024,\n\t\t\t\t\"height\": 768,\n\t\t\t\t\"refresh\": 60004\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"width\": 1920,\n\t\t\t\t\"height\": 1080,\n\t\t\t\t\"refresh\": 60000\n\t\t\t}\n\t\t],\n\t\t\"current_mode\": {\n\t\t\t\"width\": 1920,\n\t\t\t\"height\": 1080,\n\t\t\t\"refresh\": 60000\n\t\t}\n\t}\n]\n```\n\n## 4. GET_TREE\n\n*MESSAGE*++\nRetrieve a JSON representation of the tree\n\n*REPLY*++\nAn array of objects that represent the current tree. Each object represents one\nnode and will have the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- id\n:  integer\n:[ The internal unique ID for this node\n|- name\n:  string\n:  The name of the node such as the output name or window title. For the\n   scratchpad, this will be _\\_\\_i3\\_scratch_ for compatibility with i3.\n|- type\n:  string\n:  The node type. It can be _root_, _output_, _workspace_, _con_, or\n   _floating\\_con_\n|- border\n:  string\n:  The border style for the node. It can be _normal_, _none_, _pixel_, or _csd_\n|- current_border_width\n:  integer\n:  Number of pixels used for the border width\n|- layout\n:  string\n:  The node's layout. It can either be _splith_, _splitv_, _stacked_,\n   _tabbed_, or _output_\n|- orientation\n:  string\n:  The node's orientation. It can be _vertical_, _horizontal_, or _none_\n|- percent\n:  float\n:  The percentage of the node's parent that it takes up or _null_ for the root\n   and other special nodes such as the scratchpad\n|- rect\n:  object\n:  The absolute geometry of the node. The window decorations are excluded from\n   this, but borders are included.\n|- window_rect\n:  object\n:  The geometry of the content inside the node. These coordinates are relative\n   to the node itself. Window decorations and borders are outside the\n   _window_rect_\n|- deco_rect\n:  object\n:  The geometry of the decorations for the node relative to the parent node\n|- geometry\n:  object\n:  The natural geometry of the contents if it were to size itself\n|- urgent\n:  boolean\n:  Whether the node or any of its descendants has the urgent hint set. Note:\n   This may not exist when compiled without _xwayland_ support\n|- sticky\n:  boolean\n:  Whether the node is sticky (shows on all workspaces)\n|- marks\n:  array\n:  List of marks assigned to the node\n|- focused\n:  boolean\n:  Whether the node is currently focused by the default seat (_seat0_)\n|- focus\n:  array\n:  Array of child node IDs in the current focus order\n|- nodes\n:  array\n:  The tiling children nodes for the node\n|- floating_nodes\n:  array\n:  The floating children nodes for the node\n|- representation\n:  string\n:  (Only workspaces) A string representation of the layout of the workspace\n   that can be used as an aid in submitting reproduction steps for bug reports\n|- fullscreen_mode\n:  integer\n:  (Only containers and windows) The fullscreen mode of the node. 0 means none, 1 means\n   full workspace, and 2 means global fullscreen\n|- floating\n:  string\n:  Floating state of container. Can be either \"auto_off\" or \"user_on\"\n|- scratchpad_state\n:  string\n:  Whether the window is in the scratchpad. Can be either \"none\" or \"fresh\"\n|- app_id\n:  string\n:  (Only windows) For an xdg-shell window, the name of the application, if set.\n   Otherwise, _null_\n|- pid\n:  integer\n:  (Only windows) The PID of the application that owns the window\n|- foreign_toplevel_identifier\n:  string\n:  (Only windows) The ext-foreign-toplevel-list-v1 toplevel identifier of this node.\n|- visible\n:  boolean\n:  (Only windows) Whether the node is visible\n|- shell\n:  string\n:  (Only windows) The shell of the window, such as _xdg\\_shell_ or _xwayland_\n|- inhibit_idle\n:  boolean\n:  (Only windows) Whether the window is inhibiting the idle state\n|- idle_inhibitors\n:  object\n:  (Only windows) An object containing the state of the _application_ and _user_ idle inhibitors.\n    _application_ can be _enabled_ or _none_.\n    _user_ can be _focus_, _fullscreen_, _open_, _visible_ or _none_.\n|- sandbox_engine\n:  string\n:  (Only windows) The associated sandbox engine (or _null_)\n|- sandbox_app_id\n:  string\n:  (Only windows) The app ID provided by the associated sandbox engine (or _null_)\n|- sandbox_instance_id\n:  string\n:  (Only windows) The instance ID provided by the associated sandbox engine (or\n   _null_)\n|- tag\n:  string\n:  (Only windows) For an xdg-shell window, tag of the toplevel, if set.\n   Otherwise, _null_\n|- window\n:  integer\n:  (Only xwayland windows) The X11 window ID for the xwayland window\n|- window_properties\n:  object\n:  (Only xwayland windows) An object containing the _title_, _class_, _instance_,\n   _window\\_role_, _window\\_type_, and _transient\\_for_ for the window\n\n\n*Example Reply:*\n```\n{\n\t\"id\": 1,\n\t\"name\": \"root\",\n\t\"rect\": {\n\t\t\"x\": 0,\n\t\t\"y\": 0,\n\t\t\"width\": 1920,\n\t\t\"height\": 1080\n\t},\n\t\"focused\": false,\n\t\"focus\": [\n\t\t3\n\t],\n\t\"border\": \"none\",\n\t\"current_border_width\": 0,\n\t\"layout\": \"splith\",\n\t\"orientation\": \"horizontal\",\n\t\"percent\": null,\n\t\"window_rect\": {\n\t\t\"x\": 0,\n\t\t\"y\": 0,\n\t\t\"width\": 0,\n\t\t\"height\": 0\n\t},\n\t\"deco_rect\": {\n\t\t\"x\": 0,\n\t\t\"y\": 0,\n\t\t\"width\": 0,\n\t\t\"height\": 0\n\t},\n\t\"geometry\": {\n\t\t\"x\": 0,\n\t\t\"y\": 0,\n\t\t\"width\": 0,\n\t\t\"height\": 0\n\t},\n\t\"window\": null,\n\t\"urgent\": false,\n\t\"floating_nodes\": [\n\t],\n\t\"sticky\": false,\n\t\"type\": \"root\",\n\t\"nodes\": [\n\t\t{\n\t\t\t\"id\": 2147483647,\n\t\t\t\"name\": \"__i3\",\n\t\t\t\"rect\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 1920,\n\t\t\t\t\"height\": 1080\n\t\t\t},\n\t\t\t\"focused\": false,\n\t\t\t\"focus\": [\n\t\t\t\t2147483646\n\t\t\t],\n\t\t\t\"border\": \"none\",\n\t\t\t\"current_border_width\": 0,\n\t\t\t\"layout\": \"output\",\n\t\t\t\"orientation\": \"horizontal\",\n\t\t\t\"percent\": null,\n\t\t\t\"window_rect\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 0,\n\t\t\t\t\"height\": 0\n\t\t\t},\n\t\t\t\"deco_rect\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 0,\n\t\t\t\t\"height\": 0\n\t\t\t},\n\t\t\t\"geometry\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 0,\n\t\t\t\t\"height\": 0\n\t\t\t},\n\t\t\t\"window\": null,\n\t\t\t\"urgent\": false,\n\t\t\t\"floating_nodes\": [\n\t\t\t],\n\t\t\t\"sticky\": false,\n\t\t\t\"type\": \"output\",\n\t\t\t\"nodes\": [\n\t\t\t\t{\n\t\t\t\t\t\"id\": 2147483646,\n\t\t\t\t\t\"name\": \"__i3_scratch\",\n\t\t\t\t\t\"rect\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\"width\": 1920,\n\t\t\t\t\t\t\"height\": 1080\n\t\t\t\t\t},\n\t\t\t\t\t\"focused\": false,\n\t\t\t\t\t\"focus\": [\n\t\t\t\t\t],\n\t\t\t\t\t\"border\": \"none\",\n\t\t\t\t\t\"current_border_width\": 0,\n\t\t\t\t\t\"layout\": \"splith\",\n\t\t\t\t\t\"orientation\": \"horizontal\",\n\t\t\t\t\t\"percent\": null,\n\t\t\t\t\t\"window_rect\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\"width\": 0,\n\t\t\t\t\t\t\"height\": 0\n\t\t\t\t\t},\n\t\t\t\t\t\"deco_rect\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\"width\": 0,\n\t\t\t\t\t\t\"height\": 0\n\t\t\t\t\t},\n\t\t\t\t\t\"geometry\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\"width\": 0,\n\t\t\t\t\t\t\"height\": 0\n\t\t\t\t\t},\n\t\t\t\t\t\"window\": null,\n\t\t\t\t\t\"urgent\": false,\n\t\t\t\t\t\"floating_nodes\": [\n\t\t\t\t\t],\n\t\t\t\t\t\"sticky\": false,\n\t\t\t\t\t\"type\": \"workspace\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"id\": 3,\n\t\t\t\"name\": \"eDP-1\",\n\t\t\t\"rect\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 1920,\n\t\t\t\t\"height\": 1080\n\t\t\t},\n\t\t\t\"focused\": false,\n\t\t\t\"focus\": [\n\t\t\t\t4\n\t\t\t],\n\t\t\t\"border\": \"none\",\n\t\t\t\"current_border_width\": 0,\n\t\t\t\"layout\": \"output\",\n\t\t\t\"orientation\": \"none\",\n\t\t\t\"percent\": 1.0,\n\t\t\t\"window_rect\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 0,\n\t\t\t\t\"height\": 0\n\t\t\t},\n\t\t\t\"deco_rect\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 0,\n\t\t\t\t\"height\": 0\n\t\t\t},\n\t\t\t\"geometry\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"width\": 0,\n\t\t\t\t\"height\": 0\n\t\t\t},\n\t\t\t\"window\": null,\n\t\t\t\"urgent\": false,\n\t\t\t\"floating_nodes\": [\n\t\t\t],\n\t\t\t\"sticky\": false,\n\t\t\t\"type\": \"output\",\n\t\t\t\"active\": true,\n\t\t\t\"primary\": false,\n\t\t\t\"make\": \"Unknown\",\n\t\t\t\"model\": \"0x38ED\",\n\t\t\t\"serial\": \"0x00000000\",\n\t\t\t\"scale\": 1.0,\n\t\t\t\"transform\": \"normal\",\n\t\t\t\"current_workspace\": \"1\",\n\t\t\t\"modes\": [\n\t\t\t\t{\n\t\t\t\t\t\"width\": 1920,\n\t\t\t\t\t\"height\": 1080,\n\t\t\t\t\t\"refresh\": 60052\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"current_mode\": {\n\t\t\t\t\"width\": 1920,\n\t\t\t\t\"height\": 1080,\n\t\t\t\t\"refresh\": 60052\n\t\t\t},\n\t\t\t\"nodes\": [\n\t\t\t\t{\n\t\t\t\t\t\"id\": 4,\n\t\t\t\t\t\"name\": \"1\",\n\t\t\t\t\t\"rect\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 23,\n\t\t\t\t\t\t\"width\": 1920,\n\t\t\t\t\t\t\"height\": 1057\n\t\t\t\t\t},\n\t\t\t\t\t\"focused\": false,\n\t\t\t\t\t\"focus\": [\n\t\t\t\t\t\t6,\n\t\t\t\t\t\t5\n\t\t\t\t\t],\n\t\t\t\t\t\"border\": \"none\",\n\t\t\t\t\t\"current_border_width\": 0,\n\t\t\t\t\t\"layout\": \"splith\",\n\t\t\t\t\t\"orientation\": \"horizontal\",\n\t\t\t\t\t\"percent\": null,\n\t\t\t\t\t\"window_rect\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\"width\": 0,\n\t\t\t\t\t\t\"height\": 0\n\t\t\t\t\t},\n\t\t\t\t\t\"deco_rect\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\"width\": 0,\n\t\t\t\t\t\t\"height\": 0\n\t\t\t\t\t},\n\t\t\t\t\t\"geometry\": {\n\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\"width\": 0,\n\t\t\t\t\t\t\"height\": 0\n\t\t\t\t\t},\n\t\t\t\t\t\"window\": null,\n\t\t\t\t\t\"urgent\": false,\n\t\t\t\t\t\"floating_nodes\": [\n\t\t\t\t\t],\n\t\t\t\t\t\"sticky\": false,\n\t\t\t\t\t\"num\": 1,\n\t\t\t\t\t\"output\": \"eDP-1\",\n\t\t\t\t\t\"type\": \"workspace\",\n\t\t\t\t\t\"representation\": \"H[URxvt termite]\",\n\t\t\t\t\t\"nodes\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"id\": 5,\n\t\t\t\t\t\t\t\"name\": \"urxvt\",\n\t\t\t\t\t\t\t\"rect\": {\n\t\t\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\t\t\"y\": 23,\n\t\t\t\t\t\t\t\t\"width\": 960,\n\t\t\t\t\t\t\t\t\"height\": 1057\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"focused\": false,\n\t\t\t\t\t\t\t\"focus\": [\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"border\": \"normal\",\n\t\t\t\t\t\t\t\"current_border_width\": 2,\n\t\t\t\t\t\t\t\"layout\": \"none\",\n\t\t\t\t\t\t\t\"orientation\": \"none\",\n\t\t\t\t\t\t\t\"percent\": 0.5,\n\t\t\t\t\t\t\t\"window_rect\": {\n\t\t\t\t\t\t\t\t\"x\": 2,\n\t\t\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\t\t\"width\": 956,\n\t\t\t\t\t\t\t\t\"height\": 1030\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"deco_rect\": {\n\t\t\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\t\t\"width\": 960,\n\t\t\t\t\t\t\t\t\"height\": 25\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"geometry\": {\n\t\t\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\t\t\"width\": 1124,\n\t\t\t\t\t\t\t\t\"height\": 422\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"window\": 4194313,\n\t\t\t\t\t\t\t\"urgent\": false,\n\t\t\t\t\t\t\t\"floating_nodes\": [\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"sticky\": false,\n\t\t\t\t\t\t\t\"type\": \"con\",\n\t\t\t\t\t\t\t\"fullscreen_mode\": 0,\n\t\t\t\t\t\t\t\"pid\": 23959,\n\t\t\t\t\t\t\t\"app_id\": null,\n\t\t\t\t\t\t\t\"visible\": true,\n\t\t\t\t\t\t\t\"shell\": \"xwayland\",\n\t\t\t\t\t\t\t\"inhibit_idle\": true,\n\t\t\t\t\t\t\t\"idle_inhibitors\": {\n\t\t\t\t\t\t\t\t\"application\": \"none\",\n\t\t\t\t\t\t\t\t\"user\": \"visible\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"window_properties\": {\n\t\t\t\t\t\t\t\t\"class\": \"URxvt\",\n\t\t\t\t\t\t\t\t\"instance\": \"urxvt\",\n\t\t\t\t\t\t\t\t\"title\": \"urxvt\",\n\t\t\t\t\t\t\t\t\"transient_for\": null\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"nodes\": [\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"id\": 6,\n\t\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\t\"rect\": {\n\t\t\t\t\t\t\t\t\"x\": 960,\n\t\t\t\t\t\t\t\t\"y\": 23,\n\t\t\t\t\t\t\t\t\"width\": 960,\n\t\t\t\t\t\t\t\t\"height\": 1057\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"focused\": true,\n\t\t\t\t\t\t\t\"focus\": [\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"border\": \"normal\",\n\t\t\t\t\t\t\t\"current_border_width\": 2,\n\t\t\t\t\t\t\t\"layout\": \"none\",\n\t\t\t\t\t\t\t\"orientation\": \"none\",\n\t\t\t\t\t\t\t\"percent\": 0.5,\n\t\t\t\t\t\t\t\"window_rect\": {\n\t\t\t\t\t\t\t\t\"x\": 2,\n\t\t\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\t\t\"width\": 956,\n\t\t\t\t\t\t\t\t\"height\": 1030\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"deco_rect\": {\n\t\t\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\t\t\"width\": 960,\n\t\t\t\t\t\t\t\t\"height\": 25\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"geometry\": {\n\t\t\t\t\t\t\t\t\"x\": 0,\n\t\t\t\t\t\t\t\t\"y\": 0,\n\t\t\t\t\t\t\t\t\"width\": 817,\n\t\t\t\t\t\t\t\t\"height\": 458\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"window\": null,\n\t\t\t\t\t\t\t\"urgent\": false,\n\t\t\t\t\t\t\t\"floating_nodes\": [\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"sticky\": false,\n\t\t\t\t\t\t\t\"type\": \"con\",\n\t\t\t\t\t\t\t\"fullscreen_mode\": 0,\n\t\t\t\t\t\t\t\"pid\": 25370,\n\t\t\t\t\t\t\t\"app_id\": \"termite\",\n\t\t\t\t\t\t\t\"visible\": true,\n\t\t\t\t\t\t\t\"shell\": \"xdg_shell\",\n\t\t\t\t\t\t\t\"inhibit_idle\": false,\n\t\t\t\t\t\t\t\"idle_inhibitors\": {\n\t\t\t\t\t\t\t\t\"application\": \"none\",\n\t\t\t\t\t\t\t\t\"user\": \"fullscreen\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"nodes\": [\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}\n```\n\n## 5. GET_MARKS\n\n*MESSAGE*++\nRetrieve the currently set marks\n\n*REPLY*++\nAn array of marks current set. Since each mark can only be set for one\ncontainer, this is a set so each value is unique and the order is undefined.\n\n*Example Reply:*\n```\n[\n\t\"one\",\n\t\"test\"\n]\n```\n\n## 6. GET_BAR_CONFIG (WITHOUT A PAYLOAD)\n\n*MESSAGE*++\nWhen sending without a payload, this retrieves the list of configured bar IDs\n\n*REPLY*++\nAn array of bar IDs, which are strings\n\n*Example Reply:*\n```\n[\n\t\"bar-0\",\n\t\"bar-1\"\n]\n```\n\n## 6. GET_BAR_CONFIG (WITH A PAYLOAD)\n\n*MESSAGE*++\nWhen sent with a bar ID as the payload, this retrieves the config associated\nwith the specified by the bar ID in the payload. This is used by swaybar, but\ncould also be used for third party bars\n\n*REPLY*++\nAn object that represents the configuration for the bar with the bar ID sent as\nthe payload. The following properties exists and more information about what\ntheir value mean can be found in *sway-bar*(5):\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- id\n:  string\n:[ The bar ID\n|- mode\n:  string\n:  The mode for the bar. It can be _dock_, _hide_, or _invisible_\n|- position\n:  string\n:  The bar's position. It can currently either be _bottom_ or _top_\n|- status_command\n:  string\n:  The command which should be run to generate the status line\n|- font\n:  string\n:  The font to use for the text on the bar\n|- workspace_buttons\n:  boolean\n:  Whether to display the workspace buttons on the bar\n|- workspace_min_width\n:  integer\n:  Minimum width in px for the workspace buttons on the bar\n|- binding_mode_indicator\n:  boolean\n:  Whether to display the current binding mode on the bar\n|- verbose\n:  boolean\n:  For i3 compatibility, this will be the boolean value _false_.\n|- colors\n:  object\n:  An object containing the _#RRGGBBAA_ colors to use for the bar. See below\n   for more information\n|- gaps\n:  object\n:  An object representing the gaps for the bar consisting of _top_, _right_,\n   _bottom_, and _left_.\n|- bar_height\n:  integer\n:  The absolute height to use for the bar or _0_ to automatically size based\n   on the font\n|- status_padding\n:  integer\n:  The vertical padding to use for the status line\n|- status_edge_padding\n:  integer\n:  The horizontal padding to use for the status line when at the end of an\n   output\n\n\nThe colors object contains the following properties, which are all strings\ncontaining the _#RRGGBBAA_ representation of the color:\n[- *PROPERTY*\n:- *DESCRIPTION*\n|- background\n:[ The color to use for the bar background on unfocused outputs\n|- statusline\n:  The color to use for the status line text on unfocused outputs\n|- separator\n:  The color to use for the separator text on unfocused outputs\n|- focused_background\n:  The color to use for the background of the bar on the focused output\n|- focused_statusline\n:  The color to use for the status line text on the focused output\n|- focused_separator\n:  The color to use for the separator text on the focused output\n|- focused_workspace_text\n:  The color to use for the text of the focused workspace button\n|- focused_workspace_bg\n:  The color to use for the background of the focused workspace button\n|- focused_workspace_border\n:  The color to use for the border of the focused workspace button\n|- active_workspace_text\n:  The color to use for the text of the workspace buttons for the visible\n   workspaces on unfocused outputs\n|- active_workspace_bg\n:  The color to use for the background of the workspace buttons for the visible\n   workspaces on unfocused outputs\n|- active_workspace_border\n:  The color to use for the border of the workspace buttons for the visible\n   workspaces on unfocused outputs\n|- inactive_workspace_text\n:  The color to use for the text of the workspace buttons for workspaces that\n   are not visible\n|- inactive_workspace_bg\n:  The color to use for the background of the workspace buttons for workspaces\n   that are not visible\n|- inactive_workspace_border\n:  The color to use for the border of the workspace buttons for workspaces\n   that are not visible\n|- urgent_workspace_text\n:  The color to use for the text of the workspace buttons for workspaces that\n   contain an urgent window\n|- urgent_workspace_bg\n:  The color to use for the background of the workspace buttons for workspaces\n   that contain an urgent window\n|- urgent_workspace_border\n:  The color to use for the border of the workspace buttons for workspaces that\n   contain an urgent window\n|- binding_mode_text\n:  The color to use for the text of the binding mode indicator\n|- binding_mode_bg\n:  The color to use for the background of the binding mode indicator\n|- binding_mode_border\n:  The color to use for the border of the binding mode indicator\n\n\n*Example Reply:*\n```\n{\n\t\"id\": \"bar-0\",\n\t\"mode\": \"dock\",\n\t\"position\": \"top\",\n\t\"status_command\": \"while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done\",\n\t\"font\": \"monospace 10\",\n\t\"gaps\": {\n\t\t\"top\": 0,\n\t\t\"right\": 0,\n\t\t\"bottom\": 0,\n\t\t\"left\": 0\n\t},\n\t\"bar_height\": 0,\n\t\"status_padding\": 1,\n\t\"status_edge_padding\": 3,\n\t\"workspace_buttons\": true,\n\t\"workspace_min_width\": 0,\n\t\"binding_mode_indicator\": true,\n\t\"verbose\": false,\n\t\"pango_markup\": false,\n\t\"colors\": {\n\t\t\"background\": \"#323232ff\",\n\t\t\"statusline\": \"#ffffffff\",\n\t\t\"separator\": \"#666666ff\",\n\t\t\"focused_background\": \"#323232ff\",\n\t\t\"focused_statusline\": \"#ffffffff\",\n\t\t\"focused_separator\": \"#666666ff\",\n\t\t\"focused_workspace_border\": \"#4c7899ff\",\n\t\t\"focused_workspace_bg\": \"#285577ff\",\n\t\t\"focused_workspace_text\": \"#ffffffff\",\n\t\t\"inactive_workspace_border\": \"#32323200\",\n\t\t\"inactive_workspace_bg\": \"#32323200\",\n\t\t\"inactive_workspace_text\": \"#5c5c5cff\",\n\t\t\"active_workspace_border\": \"#333333ff\",\n\t\t\"active_workspace_bg\": \"#5f676aff\",\n\t\t\"active_workspace_text\": \"#ffffffff\",\n\t\t\"urgent_workspace_border\": \"#2f343aff\",\n\t\t\"urgent_workspace_bg\": \"#900000ff\",\n\t\t\"urgent_workspace_text\": \"#ffffffff\",\n\t\t\"binding_mode_border\": \"#2f343aff\",\n\t\t\"binding_mode_bg\": \"#900000ff\",\n\t\t\"binding_mode_text\": \"#ffffffff\"\n\t},\n}\n```\n\n## 7. GET_VERSION\n\n*MESSAGE*++\nRetrieve version information about the sway process\n\n*REPLY*++\nAn object containing the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- major\n:  integer\n:[ The major version of the sway process\n|- minor\n:  integer\n:  The minor version of the sway process\n|- patch\n:  integer\n:  The patch version of the sway process\n|- human_readable\n:  string\n:  A human readable version string that will likely contain more useful\n   information such as the git commit short hash and git branch\n|- loaded_config_file_name\n:  string\n:  The path to the loaded config file\n\n\n*Example Reply:*\n```\n{\n\t\"human_readable\": \"1.0-rc1-117-g2f7247e0 (Feb 24 2019, branch 'master')\",\n\t\"major\": 1,\n\t\"minor\": 0,\n\t\"patch\": 0,\n\t\"loaded_config_file_name\": \"/home/redsoxfan/.config/sway/config\"\n}\n```\n\n## 8. GET_BINDING_MODES\n\n*MESSAGE*++\nRetrieve the list of binding modes that currently configured\n\n*REPLY*++\nAn array of strings, with each string being the name of a binding mode. This\nwill always contain at least one mode (currently _default_), which is the\ndefault binding mode\n\n*Example Reply:*\n```\n[\n\t\"default\",\n\t\"resize\",\n]\n```\n\n## 9. GET_CONFIG\n\n*MESSAGE*++\nRetrieve the contents of the config that was last loaded\n\n*REPLY*++\nAn object with a single string property containing the contents of the config\n\n*Example Reply:*\n```\n{\n\t\"config\": \"set $mod Mod4\\\\nbindsym $mod+q exit\\\\n\"\n}\n```\n\n## 10. SEND_TICK\n\n*MESSAGE*++\nIssues a _TICK_ event to all clients subscribing to the event to ensure that\nall events prior to the tick were received. If a payload is given, it will be\nincluded in the _TICK_ event\n\n*REPLY*++\nA single object contains the property _success_, which is a boolean value\nindicating whether the _TICK_ event was sent.\n\n*Example Reply:*\n```\n{\n\t\"success\": true\n}\n```\n\n## 11. SYNC\n\n*MESSAGE*++\nFor i3 compatibility, this command will just return a failure object since it\ndoes not make sense to implement in sway due to the X11 nature of the command.\nIf you are curious about what this IPC command does in i3, refer to the i3\ndocumentation.\n\n*REPLY*++\nA single object that contains the property _success_, which is set to the\nboolean value _false_.\n\n*Exact Reply:*\n```\n{\n\t\"success\": false\n}\n```\n\n## 12. GET_BINDING_STATE\n\n*MESSAGE*++\nReturns the currently active binding mode.\n\n*REPLY*++\nA single object that contains the property _name_, which is set to the\ncurrently active binding mode as a string.\n\n*Example Reply:*\n```\n{\n\t\"name\": \"default\"\n}\n```\n\n\n## 100. GET_INPUTS\n\n*MESSAGE*++\nRetrieve a list of the input devices currently available\n\n*REPLY*++\nAn array of objects corresponding to each input device. Each object has the\nfollowing properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- identifier\n:  string\n:[ The identifier for the input device\n|- name\n:  string\n:  The human readable name for the device\n|- vendor\n:  integer\n:  The vendor code for the input device\n|- product\n:  integer\n:  The product code for the input device\n|- type\n:  string\n:  The device type. Currently this can be _keyboard_, _pointer_, _touch_,\n   _tablet\\_tool_, _tablet\\_pad_, or _switch_\n|- xkb_active_layout_name\n:  string\n:  (Only keyboards) The name of the active keyboard layout in use\n|- xkb_layout_names\n:  array\n:  (Only keyboards) A list a layout names configured for the keyboard\n|- xkb_active_layout_index\n:  integer\n:  (Only keyboards) The index of the active keyboard layout in use\n|- scroll_factor\n:  floating\n:  (Only pointers) Multiplier applied on scroll event values.\n|- libinput\n:  object\n:  (Only libinput devices) An object describing the current device settings.\n   See below for more information\n\nThe _libinput_ object describes the device configuration for libinput devices.\nOnly properties that are supported for the device will be added to the object.\nIn addition to the possible options listed, all string properties may also be\n_unknown_, in the case that a new option is added to libinput. See\n*sway-input*(5) for information on the meaning of the possible values. The\nfollowing properties will be included for devices that support them:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- send_events\n:  string\n:[ Whether events are being sent by the device. It can be _enabled_,\n   _disabled_, or _disabled\\_on\\_external\\_mouse_\n|- tap\n:  string\n:  Whether tap to click is enabled. It can be _enabled_ or _disabled_\n|- tap_button_map\n:  string\n:  The finger to button mapping in use for tapping. It can be _lmr_ or _lrm_\n|- tap_drag\n:  string\n:  Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_\n|- tap_drag_lock\n:  string\n:  Whether drag-lock is enabled. It can be _enabled_, _disabled_ or\n   _enabled_sticky_\n|- accel_speed\n:  double\n:  The pointer-acceleration in use\n|- accel_profile\n:  string\n:  The acceleration profile in use. It can be _none_, _flat_, or _adaptive_\n|- natural_scroll\n:  string\n:  Whether natural scrolling is enabled. It can be _enabled_ or _disabled_\n|- left_handed\n:  string\n:  Whether left-handed mode is enabled. It can be _enabled_ or _disabled_\n|- click_method\n:  string\n:  The click method in use. It can be _none_, _button_areas_, or _clickfinger_\n|- click_button_map\n:  string\n:  The finger to button mapping in use for clickfinger. It can be _lmr_ or _lrm_\n|- middle_emulation\n:  string\n:  Whether middle emulation is enabled. It can be _enabled_ or _disabled_\n|- scroll_method\n:  string\n:  The scroll method in use. It can be _none_, _two_finger_, _edge_, or\n   _on_button_down_\n|- scroll_button\n:  int\n:  The scroll button to use when _scroll_method_ is _on_button_down_. This\n   will be given as an input event code\n|- scroll_button_lock\n:  string\n:  Whether scroll button lock is enabled. It can be _enabled_ or _disabled_\n|- dwt\n:  string\n:  Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_\n|- dwtp\n:  string\n:  Whether disable-while-trackpointing is enabled. It can be _enabled_ or\n  _disabled_\n|- calibration_matrix\n:  array\n:  An array of 6 floats representing the calibration matrix for absolute\n   devices such as touchscreens\n\n\n*Example Reply:*\n```\n[\n\t{\n\t\t\"identifier\": \"1:1:AT_Translated_Set_2_keyboard\",\n\t\t\"name\": \"AT Translated Set 2 keyboard\",\n\t\t\"vendor\": 1,\n\t\t\"product\": 1,\n\t\t\"type\": \"keyboard\",\n\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\"\n\t\t}\n\t},\n\t{\n\t\t\"identifier\": \"1267:5:Elan_Touchpad\",\n\t\t\"name\": \"Elan Touchpad\",\n\t\t\"vendor\": 1267,\n\t\t\"product\": 5,\n\t\t\"type\": \"pointer\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\",\n\t\t\t\"tap\": \"enabled\",\n\t\t\t\"tap_button_map\": \"lmr\",\n\t\t\t\"tap_drag\": \"enabled\",\n\t\t\t\"tap_drag_lock\": \"disabled\",\n\t\t\t\"accel_speed\": 0.0,\n\t\t\t\"accel_profile\": \"none\",\n\t\t\t\"natural_scroll\", \"disabled\",\n\t\t\t\"left_handed\": \"disabled\",\n\t\t\t\"click_method\": \"button_areas\",\n\t\t\t\"middle_emulation\": \"disabled\",\n\t\t\t\"scroll_method\": \"edge\",\n\t\t\t\"dwt\": \"enabled\",\n\t\t\t\"dwtp\": \"enabled\"\n\t\t}\n\t},\n\t{\n\t\t\"identifier\": \"3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V\",\n\t\t\"name\": \"USB2.0 VGA UVC WebCam: USB2.0 V\",\n\t\t\"vendor\": 3034,\n\t\t\"product\": 22494,\n\t\t\"type\": \"keyboard\",\n\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\"\n\t\t}\n\t},\n\t{\n\t\t\"identifier\": \"0:3:Sleep_Button\",\n\t\t\"name\": \"Sleep Button\",\n\t\t\"vendor\": 0,\n\t\t\"product\": 3,\n\t\t\"type\": \"keyboard\",\n\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\"\n\t\t}\n\t},\n\t{\n\t\t\"identifier\": \"0:5:Lid_Switch\",\n\t\t\"name\": \"Lid Switch\",\n\t\t\"vendor\": 0,\n\t\t\"product\": 5,\n\t\t\"type\": \"switch\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\"\n\t\t}\n\t},\n\t{\n\t\t\"identifier\": \"0:6:Video_Bus\",\n\t\t\"name\": \"Video Bus\",\n\t\t\"vendor\": 0,\n\t\t\"product\": 6,\n\t\t\"type\": \"keyboard\",\n\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\"\n\t\t}\n\t},\n\t{\n\t\t\"identifier\": \"0:1:Power_Button\",\n\t\t\"name\": \"Power Button\",\n\t\t\"vendor\": 0,\n\t\t\"product\": 1,\n\t\t\"type\": \"keyboard\",\n\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\"\n\t\t}\n\t}\n]\n```\n\n## 101. GET_SEATS\n\n*MESSAGE*++\nRetrieve a list of the seats currently configured\n\n*REPLY*++\nAn array of objects corresponding to each seat. There will always be at least\none seat. Each object has the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- name\n:  string\n:] The unique name for the seat\n|- capabilities\n:  integer\n:  The number of capabilities that the seat has\n|- focus\n:  integer\n:  The id of the node currently focused by the seat or _0_ when the seat is\n   not currently focused by a node (i.e. a surface layer or xwayland unmanaged\n   has focus)\n|- devices\n:  array\n:  An array of input devices that are attached to the seat. Currently, this\n   is an array of objects that are identical to those returned by _GET\\_INPUTS_\n\n\n*Example Reply:*\n```\n[\n\t{\n\t\t\"name\": \"seat0\",\n\t\t\"capabilities\": 3,\n\t\t\"focus\": 7,\n\t\t\"devices\": [\n\t\t\t{\n\t\t\t\t\"identifier\": \"1:1:AT_Translated_Set_2_keyboard\",\n\t\t\t\t\"name\": \"AT Translated Set 2 keyboard\",\n\t\t\t\t\"vendor\": 1,\n\t\t\t\t\"product\": 1,\n\t\t\t\t\"type\": \"keyboard\",\n\t\t\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\t\t\"libinput\": {\n\t\t\t\t\t\"send_events\": \"enabled\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"identifier\": \"1267:5:Elan_Touchpad\",\n\t\t\t\t\"name\": \"Elan Touchpad\",\n\t\t\t\t\"vendor\": 1267,\n\t\t\t\t\"product\": 5,\n\t\t\t\t\"type\": \"pointer\",\n\t\t\t\t\"libinput\": {\n\t\t\t\t\t\"send_events\": \"enabled\",\n\t\t\t\t\t\"tap\": \"enabled\",\n\t\t\t\t\t\"tap_button_map\": \"lmr\",\n\t\t\t\t\t\"tap_drag\": \"enabled\",\n\t\t\t\t\t\"tap_drag_lock\": \"disabled\",\n\t\t\t\t\t\"accel_speed\": 0.0,\n\t\t\t\t\t\"accel_profile\": \"none\",\n\t\t\t\t\t\"natural_scroll\", \"disabled\",\n\t\t\t\t\t\"left_handed\": \"disabled\",\n\t\t\t\t\t\"click_method\": \"button_areas\",\n\t\t\t\t\t\"middle_emulation\": \"disabled\",\n\t\t\t\t\t\"scroll_method\": \"edge\",\n\t\t\t\t\t\"dwt\": \"enabled\",\n\t\t\t\t\t\"dwtp\": \"enabled\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"identifier\": \"3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V\",\n\t\t\t\t\"name\": \"USB2.0 VGA UVC WebCam: USB2.0 V\",\n\t\t\t\t\"vendor\": 3034,\n\t\t\t\t\"product\": 22494,\n\t\t\t\t\"type\": \"keyboard\",\n\t\t\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\t\t\"libinput\": {\n\t\t\t\t\t\"send_events\": \"enabled\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"identifier\": \"0:3:Sleep_Button\",\n\t\t\t\t\"name\": \"Sleep Button\",\n\t\t\t\t\"vendor\": 0,\n\t\t\t\t\"product\": 3,\n\t\t\t\t\"type\": \"keyboard\",\n\t\t\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\t\t\"libinput\": {\n\t\t\t\t\t\"send_events\": \"enabled\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"identifier\": \"0:5:Lid_Switch\",\n\t\t\t\t\"name\": \"Lid Switch\",\n\t\t\t\t\"vendor\": 0,\n\t\t\t\t\"product\": 5,\n\t\t\t\t\"type\": \"switch\",\n\t\t\t\t\"libinput\": {\n\t\t\t\t\t\"send_events\": \"enabled\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"identifier\": \"0:6:Video_Bus\",\n\t\t\t\t\"name\": \"Video Bus\",\n\t\t\t\t\"vendor\": 0,\n\t\t\t\t\"product\": 6,\n\t\t\t\t\"type\": \"keyboard\",\n\t\t\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\t\t\"libinput\": {\n\t\t\t\t\t\"send_events\": \"enabled\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"identifier\": \"0:1:Power_Button\",\n\t\t\t\t\"name\": \"Power Button\",\n\t\t\t\t\"vendor\": 0,\n\t\t\t\t\"product\": 1,\n\t\t\t\t\"type\": \"keyboard\",\n\t\t\t\t\"xkb_active_layout_name\": \"English (US)\",\n\t\t\t\t\"libinput\": {\n\t\t\t\t\t\"send_events\": \"enabled\"\n\t\t\t\t}\n\t\t\t}\n\t\t]\n\t}\n]\n```\n\n# EVENTS\n\nEvents are a way for client to get notified of changes to sway. A client can\nsubscribe to any events it wants to be notified of changes for. The event is\nsent in the same format as a reply. The following events are currently\navailable:\n\n[- *EVENT TYPE*\n:- *NAME*\n:- *DESCRIPTION*\n|- 0x80000000\n:  workspace\n:[ Sent whenever an event involving a workspace occurs such as initialization\n   of a new workspace or a different workspace gains focus\n|- 0x80000001\n:  output\n:  Sent when outputs are updated\n|- 0x80000002\n:  mode\n:  Sent whenever the binding mode changes\n|- 0x80000003\n:  window\n:  Sent whenever an event involving a window occurs such as being reparented,\n   focused, or closed\n|- 0x80000004\n:  barconfig_update\n:  Sent whenever a bar config changes\n|- 0x80000005\n:  binding\n:  Sent when a configured binding is executed\n|- 0x80000006\n:  shutdown\n:  Sent when the ipc shuts down because sway is exiting\n|- 0x80000007\n:  tick\n:  Sent when an ipc client sends a _SEND\\_TICK_ message\n|- 0x80000014\n:  bar_state_update\n:  Send when the visibility of a bar should change due to a modifier\n|- 0x80000015\n:  input\n:  Sent when something related to input devices changes\n\n\n## 0x80000000. WORKSPACE\n\nSent whenever a change involving a workspace occurs. The event consists of a\nsingle object with the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- change\n:  string\n:[ The type of change that occurred. See below for more information\n|- current\n:  object\n:  An object representing the workspace effected or _null_ for _reload_ changes\n|- old\n:  object\n:  For a _focus_ change, this is will be an object representing the workspace\n   being switched from. Otherwise, it is _null_\n\n\nThe following change types are currently available:\n[- *TYPE*\n:- *DESCRIPTION*\n|- init\n:[ The workspace was created\n|- empty\n:  The workspace is empty and is being destroyed since it is not visible\n|- focus\n:  The workspace was focused. See the _old_ property for the previous focus\n|- move\n:  The workspace was moved to a different output\n|- rename\n:  The workspace was renamed\n|- urgent\n:  A window on the workspace has had their urgency hint set or all urgency hints\n   for windows on the workspace have been cleared\n|- reload\n:  The configuration file has been reloaded\n\n\n*Example Event:*\n```\n{\n\t\"change\": \"init\",\n\t\"old\": null,\n\t\"current\": {\n\t\t\"id\": 10,\n\t\t\"name\": \"2\",\n\t\t\"rect\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 0,\n\t\t\t\"height\": 0\n\t\t},\n\t\t\"focused\": false,\n\t\t\"focus\": [\n\t\t],\n\t\t\"border\": \"none\",\n\t\t\"current_border_width\": 0,\n\t\t\"layout\": \"splith\",\n\t\t\"percent\": null,\n\t\t\"window_rect\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 0,\n\t\t\t\"height\": 0\n\t\t},\n\t\t\"deco_rect\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 0,\n\t\t\t\"height\": 0\n\t\t},\n\t\t\"geometry\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 0,\n\t\t\t\"height\": 0\n\t\t},\n\t\t\"window\": null,\n\t\t\"urgent\": false,\n\t\t\"floating_nodes\": [\n\t\t],\n\t\t\"num\": 2,\n\t\t\"output\": \"eDP-1\",\n\t\t\"type\": \"workspace\",\n\t\t\"representation\": null,\n\t\t\"nodes\": [\n\t\t]\n\t}\n}\n```\n\n## 0x80000001. OUTPUT\n\nSent whenever an output is added, removed, or its configuration is changed.\nThe event is a single object with the property _change_, which is a string\ncontaining the reason for the change. Currently, the only value for _change_ is\n_unspecified_.\n\n*Example Event:*\n```\n{\n\t\"change\": \"unspecified\"\n}\n```\n\n## 0x80000002. MODE\n\nSent whenever the binding mode changes. The event consists of a single object\nwith the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- change\n:  string\n:[ The binding mode that became active\n|- pango_markup\n:  boolean\n:  Whether the mode should be parsed as pango markup\n\n\n*Example Event:*\n```\n{\n\t\"change\": \"default\",\n\t\"pango_markup\": false\n}\n```\n\n## 0x80000003. WINDOW\n\nSent whenever a change involving a window occurs. The event consists of a single\nobject with the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- change\n:  string\n:[ The type of change that occurred. See below for more information\n|- container\n:  object\n:  An object representing the window effected\n\n\nThe following change types are currently available:\n[- *TYPE*\n:- *DESCRIPTION*\n|- new\n:[ The window was created\n|- close\n:  The window was closed\n|- focus\n:  The window was focused\n|- title\n:  The window's title has changed\n|- fullscreen_mode\n:  The window's fullscreen mode has changed\n|- move\n:  The window has been reparented in the tree\n|- floating\n:  The window has become floating or is no longer floating\n|- urgent\n:  The window's urgency hint has changed status\n|- mark\n:  A mark has been added or removed from the window\n\n\n*Example Event:*\n```\n{\n\t\"change\": \"new\",\n\t\"container\": {\n\t\t\"id\": 12,\n\t\t\"name\": null,\n\t\t\"rect\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 0,\n\t\t\t\"height\": 0\n\t\t},\n\t\t\"focused\": false,\n\t\t\"focus\": [\n\t\t],\n\t\t\"border\": \"none\",\n\t\t\"current_border_width\": 0,\n\t\t\"layout\": \"none\",\n\t\t\"percent\": 0.0,\n\t\t\"window_rect\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 0,\n\t\t\t\"height\": 0\n\t\t},\n\t\t\"deco_rect\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 0,\n\t\t\t\"height\": 0\n\t\t},\n\t\t\"geometry\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"width\": 1124,\n\t\t\t\"height\": 422\n\t\t},\n\t\t\"window\": 4194313,\n\t\t\"urgent\": false,\n\t\t\"floating_nodes\": [\n\t\t],\n\t\t\"type\": \"con\",\n\t\t\"pid\": 19787,\n\t\t\"app_id\": null,\n\t\t\"window_properties\": {\n\t\t\t\"class\": \"URxvt\",\n\t\t\t\"instance\": \"urxvt\",\n\t\t\t\"transient_for\": null\n\t\t},\n\t\t\"nodes\": [\n\t\t]\n\t}\n}\n```\n\n## 0x80000004. BARCONFIG_UPDATE\n\nSent whenever a config for a bar changes. The event is identical to that of\n_GET_BAR_CONFIG_ when a bar ID is given as a payload. See _6. GET\\_BAR\\_CONFIG\n(WITH A PAYLOAD)_ above for more information.\n\n## 0x80000005. BINDING\n\nSent whenever a binding is executed. The event is a single object with the\nfollowing properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- change\n:  string\n:[ The change that occurred for the binding. Currently this will only be _run_\n|- command\n:  string\n:  The command associated with the binding\n|- event_state_mask\n:  array\n:  An array of strings that correspond to each modifier key for the binding\n|- input_code\n:  integer\n:  For keyboard bindcodes, this is the key code for the binding. For mouse\n   bindings, this is the X11 button number, if there is an equivalent. In all\n   other cases, this will be _0_.\n|- symbol\n:  string\n:  For keyboard bindsyms, this is the bindsym for the binding. Otherwise, this\n   will be _null_\n|- input_type\n:  string\n:  The input type that triggered the binding. This is either _keyboard_ or\n   _mouse_\n\n\n*Example Event:*\n```\n{\n\t\"change\": \"run\",\n\t\"binding\": {\n\t\t\"command\": \"workspace 2\",\n\t\t\"event_state_mask\": [\n\t\t\t\"Mod4\"\n\t\t],\n\t\t\"input_code\": 0,\n\t\t\"symbol\": \"2\",\n\t\t\"input_type\": \"keyboard\"\n\t}\n}\n```\n\n## 0x80000006. SHUTDOWN\n\nSent whenever the IPC is shutting down. The event is a single object with the\nproperty _change_, which is a string containing the reason for the shutdown.\nCurrently, the only value for _change_ is _exit_, which is issued when sway is\nexiting.\n\n*Example Event:*\n```\n{\n\t\"change\": \"exit\"\n}\n```\n\n## 0x80000007. TICK\n\nSent when first subscribing to tick events or by a _SEND\\_TICK_ message. The\nevent is a single object with the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- first\n:  boolean\n:  Whether this event was triggered by subscribing to the tick events\n|- payload\n:  string\n:  The payload given with a _SEND\\_TICK_ message, if any. Otherwise, an empty\n   string\n\n\n*Example Event:*\n```\n{\n\t\"first\": true\n\t\"payload\": \"\"\n}\n```\n\n## 0x80000014. BAR_STATE_UPDATE\n\nSent when the visibility of a bar changes due to a modifier being pressed. The\nevent is a single object with the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- id\n:  string\n:[ The bar ID effected\n|- visible_by_modifier\n:  boolean\n:  Whether the bar should be made visible due to a modifier being pressed\n\n\n*Example Event:*\n```\n{\n\t\"id\": \"bar-0\",\n\t\"visible_by_modifier\": true\n}\n```\n\n## 0x80000015. INPUT\n\nSent when something related to the input devices changes. The event is a single\nobject with the following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- change\n:  string\n:[ What has changed\n|- input\n:  object\n:  An object representing the input that is identical the ones GET_INPUTS gives\n\nThe following change types are currently available:\n[- *TYPE*\n:- *DESCRIPTION*\n|- added\n:[ The input device became available\n|- removed\n:  The input device is no longer available\n|- xkb_keymap\n:  (Keyboards only) The keymap for the keyboard has changed\n|- xkb_layout\n:  (Keyboards only) The effective layout in the keymap has changed\n|- libinput_config\n:  (libinput device only) A libinput config option for the device changed\n\n*Example Event:*\n```\n{\n\t\"change\": \"xkb_layout\",\n\t\"input\": {\n\t\t\"identifier\": \"1:1:AT_Translated_Set_2_keyboard\",\n\t\t\"name\": \"AT Translated Set 2 keyboard\",\n\t\t\"vendor\": 1,\n\t\t\"product\": 1,\n\t\t\"type\": \"keyboard\",\n\t\t\"xkb_layout_names\": [\n\t\t\t\"English (US)\",\n\t\t\t\"English (Dvorak)\"\n\t\t],\n\t\t\"xkb_active_layout_index\": 1,\n\t\t\"xkb_active_layout_name\": \"English (Dvorak)\",\n\t\t\"libinput\": {\n\t\t\t\"send_events\": \"enabled\"\n\t\t}\n\t}\n}\n```\n\n# SEE ALSO\n\n*sway*(1) *sway*(5) *sway-bar*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5)\n"
  },
  {
    "path": "sway/sway-output.5.scd",
    "content": "sway-output(5)\n\n# NAME\n\nsway-output - output configuration commands for sway\n\n# DESCRIPTION\n\nYou may combine output commands into one, like so:\n\n\toutput HDMI-A-1 mode 1920x1080 pos 1920 0 bg ~/wallpaper.png stretch\n\nYou can get a list of output names with *swaymsg -t get_outputs*. You may also\nmatch any output by using the output name \"\\*\". Additionally, \"-\" can be used\nto match the focused output by name and \"--\" can be used to match the focused\noutput by its identifier.\n\nSome outputs may have different names when disconnecting and reconnecting. To\nidentify these, the name can be substituted for a string consisting of the make,\nmodel and serial which you can get from *swaymsg -t get_outputs*. Each value\nmust be separated by one space. For example:\n\n\toutput \"Some Company ABC123 0x00000000\" pos 1920 0\n\n# COMMANDS\n\n*output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz]\n\tConfigures the specified output to use the given mode. Modes are a\n\tcombination of width and height (in pixels) and a refresh rate that your\n\tdisplay can be configured to use. For a list of available modes for each\n\toutput, use *swaymsg -t get_outputs*.\n\n\tTo set a custom mode not listed in the list of available modes, use\n\t*--custom*. You should probably only use this if you know what you're\n\tdoing.\n\n\tExamples:\n\n\t\toutput HDMI-A-1 mode 1920x1080\n\n\t\toutput HDMI-A-1 mode 1920x1080@60Hz\n\n*output* <name> modeline <clock> <hdisplay> <hsync_start> <hsync_end> <htotal> <vdisplay> <vsync_start> <vsync_end> <vtotal> <hsync> <vsync>\n\tConfigures the specified output to use the given modeline. It can be\n\tgenerated using *cvt*(1) and *gtf*(1) commands. See *xorg.conf*(5).\n\tOnly supported on DRM backend.\n\n\tExample:\n\n\t\toutput HDMI-A-1 modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync\n\n\n*output* <name> position|pos <X> <Y>\n\tPlaces the specified output at the specific position in the global\n\tcoordinate space. The cursor may only be moved between immediately\n\tadjacent outputs. If scaling is active, it has to be considered when\n\tpositioning. For example, if the scaling factor for the left output is\n\t2, the relative position for the right output has to be divided by 2.\n\tThe reference point is the top left corner so if you want the bottoms\n\taligned this has to be considered as well.\n\n\tExample:\n\n\t\toutput HDMI1 scale 2\n\n\t\toutput HDMI1 pos 0 1020 res 3200x1800\n\n\t\toutput eDP1 pos 1600 0 res 1920x1080\n\n\tNote that the left x-pos of eDP1 is 1600 = 3200/2 and the bottom y-pos is\n\t1020 + (1800 / 2) = 1920 = 0 + 1920\n\n*output* <name> scale <factor>\n\tScales the specified output by the specified scale _factor_. An integer is\n\trecommended, but fractional values are also supported. You may be better\n\tserved by setting an integer scale factor and adjusting the font size of\n\tyour applications to taste. HiDPI isn't supported with Xwayland clients\n\t(windows will blur). A fractional scale may be slightly adjusted to match\n\trequirements of the protocol.\n\n*output* <name> scale_filter linear|nearest|smart\n\tIndicates how to scale application buffers that are rendered at a scale\n\tlower than the output's configured scale, such as lo-dpi applications on\n\thi-dpi screens. Linear is smoother and blurrier, nearest (also known as\n\tnearest neighbor) is sharper and blockier. Setting \"smart\" will apply\n\tnearest scaling when the output has an integer scale factor, otherwise\n\tlinear. The default is \"smart\".\n\n*output* <name> subpixel rgb|bgr|vrgb|vbgr|none\n\tManually sets the subpixel hinting for the specified output. This value is\n\tusually auto-detected, but some displays may misreport their subpixel\n\tgeometry. Using the correct subpixel hinting allows for sharper text.\n\tIncorrect values will result in blurrier text. When changing this via\n\t*swaymsg*, some applications may need to be restarted to use the new value.\n\n*output* <name> background|bg <file> <mode> [<fallback_color>]\n\tSets the wallpaper for the given output to the specified file, using the\n\tgiven scaling mode (one of \"stretch\", \"fill\", \"fit\", \"center\", \"tile\"). If\n\tthe specified file cannot be accessed or if the image does not fill the entire\n\toutput, a fallback color may be provided to cover the rest of the output.\n\t_fallback_color_ should be specified as _#RRGGBB_. Alpha is not supported.\n\n*output* <name> background|bg <color> solid_color\n\tSets the background of the given output to the specified color. _color_\n\tshould be specified as _#RRGGBB_. Alpha is not supported.\n\n*output* <name> transform <transform> [clockwise|anticlockwise]\n\tSets the background transform to the given value. Can be one of \"90\", \"180\",\n\t\"270\" for rotation; or \"flipped\", \"flipped-90\", \"flipped-180\", \"flipped-270\"\n\tto apply a rotation and flip, or \"normal\" to apply no transform. The\n\trotation is performed clockwise. If a single output is chosen and a\n\trotation direction is specified (_clockwise_ or _anticlockwise_) then the\n\ttransform is added or subtracted from the current transform (this cannot be\n\tused directly in the configuration file).\n\n*output* <name> disable|enable\n\tEnables or disables the specified output (all outputs are enabled by\n\tdefault).\n\n\tAs opposed to the _power_ command, the output will lose its current\n\tworkspace and windows.\n\n*output* <name> toggle\n\tToggle the specified output.\n\n*output* <name> power on|off|toggle\n\tTurns on or off the specified output.\n\n\tAs opposed to the _enable_ and _disable_ commands, the output keeps its\n\tcurrent workspaces and windows.\n\n*output* <name> dpms on|off|toggle\n\tDeprecated. Alias for _power_.\n\n*output* <name> max_render_time off|<msec>\n\tControls when sway composites the output, as a positive number of\n\tmilliseconds before the next display refresh. A smaller number leads to\n\tfresher composited frames and lower perceived input latency, but if set too\n\tlow, sway may not finish compositing in time for display refresh, leading to\n\tdelayed frames.\n\n\tWhen set to off, sway composites immediately after display refresh,\n\tmaximizing time available for compositing.\n\n\tTo adjust when applications are instructed to render, see *max_render_time*\n\tin *sway*(5).\n\n\tTo set this up for optimal latency:\n\t. Launch some _full-screen_ application that renders continuously, like\n\t  *glxgears*.\n\t. Start with *max_render_time 1*. Increment by *1* if you see frame\n\t  drops.\n\n\tThis setting only has an effect on Wayland and DRM backends, as support for\n\tpresentation timestamps and predicted output refresh rate is required.\n\n*output* <name> adaptive_sync on|off|toggle\n\tEnables or disables adaptive synchronization (often referred to as Variable\n\tRefresh Rate, or by the vendor-specific names FreeSync/G-Sync).\n\n\tAdaptive sync allows clients to submit frames a little too late without\n\thaving to wait a whole refresh period to display it on screen. Enabling\n\tadaptive sync can improve latency, but can cause flickering on some\n\thardware.\n\n*output* <name> render_bit_depth 6|8|10\n\tControls the maximum color channel bit depth at which frames are\n\trendered; the default is currently 8 bits per channel.\n\n\tSetting higher values will not have an effect if hardware and software lack\n\tsupport for such bit depths. Successfully increasing the render bit depth\n\twill not necessarily increase the bit depth of the frames sent to a display.\n\tAn increased render bit depth may provide smoother rendering of gradients,\n\tand screenshots which can more precisely store the colors of programs\n\twhich display high bit depth colors.\n\n\tWarnings: this can break screenshot/screencast programs which have not been\n\tupdated to work with different bit depths. This command is experimental,\n\tand may be removed or changed in the future.\n\n*output* <name> color_profile [--device-primaries] gamma22|srgb\n\tSets the color profile for an output. The default is _gamma22_.\n\n\t_--device-primaries_ will use the output's self-reported color primaries\n\twhen available (e.g. from display EDID).\n\n\tNot all renderers support this feature; currently it only works with the\n\tthe Vulkan renderer. It is not compatible with HDR support features.\n\n*output* <name> color_profile icc <file>\n\tSets the color profile for an output.\n\n\t<file> should be a path to a display ICC profile.\n\n\tNot all renderers support this feature; currently it only works with the\n\tthe Vulkan renderer. Even where supported, the application of the color\n\tprofile may be inaccurate.\n\n\tThis command is experimental, and may be removed or changed in the future. It\n\tmay have no effect or produce unexpected output when used together with future\n\tHDR support features.\n\n*output* <name> allow_tearing yes|no\n\tAllows or disallows screen tearing as a result of immediate page flips,\n\tand an immediate presentation mode from a client. The default is that no\n\tscreen tearing is allowed.\n\n\tWith immediate page flips, frames from the client are presented as soon\n\tas possible instead of synchronizing with the monitor's vblank interval\n\t(VSync).\n\n\tIt is recommended to set *max_render_time* to *off*. In that case a page flip\n\thappens as soon as a client updates. Otherwise, tearing will only happen if\n\trendering takes longer than the configured milliseconds before the next\n\tdisplay refresh.\n\n\tTo adjust whether tearing is allowed for specific applications, see\n\t*allow_tearing* in *sway*(5). Note that tearing will only be enabled\n\twhen it's allowed for both the output and the application.\n\n\tThis setting only has effect when a window is fullscreen on the output.\n\n*output* <name> hdr on|off|toggle\n\tEnables or disables HDR (High Dynamic Range). HDR enables a larger color\n\tgamut and brightness range. HDR uses the BT2020 primaries and the PQ\n\ttransfer function.\n\n\tWhen HDR is enabled, _render_bit_depth_ is implicitly set to 10 unless\n\texplicitly configured. Using a lower render bit depth may result in color\n\tbanding artifacts.\n\n\tHDR needs to be supported by the output and renderer to be enabled.\n\n# SEE ALSO\n\n*sway*(5) *sway-input*(5)\n"
  },
  {
    "path": "sway/sway.1.scd",
    "content": "sway(1)\n\n# NAME\n\nsway - An i3-compatible Wayland compositor\n\n# SYNOPSIS\n\n*sway* [options...] [command]\n\n# OPTIONS\n\n*-h, --help*\n\tShow help message and quit.\n\n*-c, --config* <config>\n\tSpecifies a config file.\n\n*-C, --validate*\n\tCheck the validity of the config file, then exit.\n\n*-d, --debug*\n\tEnables full logging, including debug information.\n\n*-v, --version*\n\tShow the version number and quit.\n\n*-V, --verbose*\n\tEnables more verbose logging.\n\n*--get-socketpath*\n\tGets the IPC socket path and prints it, then exits.\n\n# DESCRIPTION\n\nsway was created to fill the need of an i3-like window manager for Wayland. The\nupstream i3 developers have no intention of porting i3 to Wayland, and projects\nproposed by others ended up as vaporware. Many thanks to the i3 folks for\nproviding such a great piece of software, so good that your users would rather\nwrite an entirely new window manager from scratch that behaved _exactly_ like i3\nrather than switch to something else.\n\nYou can run sway directly from a tty, or via a Wayland-compatible login manager.\n\n# CONFIGURATION\n\nsway searches for a config file in the following locations, in this order:\n\n. ~/.sway/config\n. $XDG_CONFIG_HOME/sway/config (suggested location)\n. ~/.i3/config\n. $XDG_CONFIG_HOME/i3/config\n. /etc/sway/config\n. /etc/i3/config\n\nIf unset, $XDG_CONFIG_HOME defaults to *~/.config*.\n\nAn error is raised when no config file is found. The recommended default\nconfiguration is usually installed to */etc/sway/config*; you are encouraged to\ncopy this to *~/.config/sway/config* and edit it from there.\n\nFor information on the config file format, see *sway*(5).\n\n# IPC COMMANDS\n\nThough *swaymsg*(1) is generally preferred, you may run *sway* _command_ to\nsend _command_ to the running instance of sway. You can also issue commands\nwith *i3-msg*(1) or even with *i3*(1).\n\n# ENVIRONMENT\n\nThe following environment variables have an effect on sway:\n\n_SWAYSOCK_\n\tSpecifies the path to the sway IPC socket.\n\n_XKB\\_DEFAULT\\_RULES_, _XKB\\_DEFAULT\\_MODEL_, _XKB\\_DEFAULT\\_LAYOUT_,\n_XKB\\_DEFAULT\\_VARIANT_, _XKB\\_DEFAULT\\_OPTIONS_\n\tConfigures the xkb keyboard settings. See *xkeyboard-config*(7). The\n\tpreferred way to configure the keyboard is via the configuration file, see\n\t*sway-input*(5).\n\nThe following environment variables are set by sway:\n\n_DISPLAY_\n\tIf compiled with Xwayland support and Xwayland is not disabled by the\n\tconfig, this will be set to the name of the X display used for Xwayland.\n\n_I3SOCK_\n\tFor compatibility with i3, specifies the path to the sway IPC socket.\n\n_SWAYSOCK_\n\tSpecifies the path to the sway IPC socket.\n\n_WAYLAND_DISPLAY_\n\tSpecifies the name of the Wayland display that sway is running on.\n\n_XCURSOR_SIZE_\n\tSpecifies the configured cursor size.\n\n_XCURSOR_THEME_\n\tSpecifies the configured cursor theme.\n\n# AUTHORS\n\nMaintained by Simon Ser <contact@emersion.fr>, who is assisted by other open\nsource contributors. For more information about sway development, see\n<https://github.com/swaywm/sway>.\n\n# SEE ALSO\n\n*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5)\n*sway-ipc*(7)\n"
  },
  {
    "path": "sway/sway.5.scd",
    "content": "sway(5)\n\n# NAME\n\nsway - configuration file and commands\n\n# DESCRIPTION\n\nA sway configuration file is a list of sway commands that are executed by sway\non startup.  These commands usually consist of setting your preferences and\nsetting key bindings. An example config is likely present in /etc/sway/config\nfor you to check out.\n\nLines in the configuration file might be extended through multiple lines by\nadding a '\\\\' character at the end of line. e.g.:\n\n```\nbindsym Shift+XF86AudioRaiseVolume exec \\\\\n\tpactl set-sink-volume @DEFAULT_SINK@ -1%\n```\n\nCommands can also be given as a block in the form *command { <subcommands...>\n}*. Anything before the opening *{* will be prepended to the lines inside the\nblock. For example:\n\n```\noutput eDP-1 {\n\tbackground ~/wallpaper.png fill\n\tresolution 1920x1080\n}\n```\n\nis identical to\n\n```\noutput eDP-1 background ~/wallpaper.png fill\noutput eDP-1 resolution 1920x1080\n```\n\nThese commands can be executed in your config file, via *swaymsg*(1), or via\nthe bindsym command.\n\n# COMMAND CONVENTIONS\n\nCommands are split into several arguments using spaces. You can enclose\narguments with quotation marks (*\"...\"* or *'...'*) to add spaces to a single\nargument. You may also run several commands in order by separating each with\n*,* or *;*. Criteria is retained across commands separated by *,*, but will be\nreset (and allow for new criteria, if desired) for commands separated by a *;*.\n\nThroughout the documentation, *|* is used to distinguish between arguments for\nwhich you may only select one. *[...]* is used for optional arguments, and\n*<...>* for arguments where you are expected to supply some value.\n\n# COMMANDS\n\nThis section only lists general commands. For input and output commands, refer\nto *sway-input*(5) and *sway-output*(5).\n\n## Config only commands\nThe following commands may only be used in the configuration file.\n\n*bar* [<bar-id>] <bar-subcommands...>\n\tFor details on bar subcommands, see *sway-bar*(5).\n\n*default_orientation* horizontal|vertical|auto\n\tSets the default container layout for tiled containers.\n\n*include* <paths...>\n\tInclude files from _paths_. _paths_ can include either a full path or a\n\tpath relative to the parent config, and expands shell syntax (see\n\t*wordexp*(3) for details). The same include file can only be included once;\n\tsubsequent attempts will be ignored.\n\n*swaybg_command* <command>\n\tExecutes custom background _command_. Default is _swaybg_. Refer to\n\t*sway-output*(5) for more information.\n\n\tIt can be disabled by setting the command to a single dash:\n\t_swaybg\\_command -_\n\n*swaynag_command* <command>\n\tExecutes custom command for _swaynag_. Default is _swaynag_. Additional\n\targuments may be appended to the end. This should only be used to either\n\tdirect sway to call swaynag from a custom path or to provide additional\n\targuments. This should be placed at the top of the config for the best\n\tresults.\n\n\tIt can be disabled by setting the command to a single dash:\n\t_swaynag\\_command -_\n\n*workspace_layout* default|stacking|tabbed\n\tSpecifies the initial layout for new containers in an empty workspace.\n\n*xwayland* enable|disable|force\n\tEnables or disables Xwayland support, which allows X11 applications to be\n\tused. _enable_ will lazily load Xwayland so Xwayland will not be launched\n\tuntil the first client attempts to connect. In some cases, such as slower\n\tmachines, it may be desirable to have Xwayland started immediately by\n\tusing _force_ instead of _enable_.\n\n## Runtime only commands\nThe following commands cannot be used directly in the configuration file.\nThey are expected to be used with *bindsym* or at runtime through *swaymsg*(1).\n\n*border* none|normal|csd|pixel [<n>]\n\tSet border style for focused window. _normal_ includes a border of\n\tthickness _n_ and a title bar. _pixel_ is a border without title bar _n_\n\tpixels thick. The title bar always shows in stacking or tabbed layouts.\n\t_csd_ is short for client-side-decorations, which allows the client to draw\n\tits own decorations. Default is _normal_ with border thickness 2.\n\n*border* toggle\n\tCycles through the available border styles.\n\n*exit*\n\tExit sway and end your Wayland session.\n\n*floating* enable|disable|toggle\n\tMake focused window floating, non-floating, or the opposite of what it is now.\n\n<criteria> *focus*\n\tMoves focus to the container that matches the specified criteria.\n\n*focus* up|right|down|left\n\tMoves focus to the next container in the specified direction.\n\n*focus* prev|next [sibling]\n\tMoves focus to the previous or next container in the current layout. By default,\n\tthe last active child of the newly focused container will be focused. The _sibling_\n\toption indicates not to immediately focus a child of the container.\n\n*focus* child\n\tMoves focus to the last-focused child of the focused container.\n\n*focus* parent\n\tMoves focus to the parent of the focused container.\n\n*focus* output up|right|down|left\n\tMoves focus to the next output in the specified direction.\n\n*focus* output <name>\n\tMoves focus to the named output.\n\n*focus* tiling\n\tSets focus to the last focused tiling container.\n\n*focus* floating\n\tSets focus to the last focused floating container.\n\n*focus* mode_toggle\n\tMoves focus between the floating and tiled layers.\n\n*fullscreen* [enable|disable|toggle] [global]\n\tMakes focused window fullscreen, non-fullscreen, or the opposite of what it\n\tis now. If no argument is given, it does the same as _toggle_. If _global_\n\tis specified, the window will be fullscreen across all outputs.\n\n*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current\nset|plus|minus|toggle <amount>\n\tChanges the _inner_ or _outer_ gaps for either _all_ workspaces or the\n\t_current_ workspace. _outer_ gaps can be altered per side with _top_,\n\t_right_, _bottom_, and _left_ or per direction with _horizontal_ and\n\t_vertical_.\n\n*inhibit_idle* focus|fullscreen|open|none|visible\n\tSet/unset an idle inhibitor for the window. _focus_ will inhibit idle when\n\tthe window is focused by any seat. _fullscreen_ will inhibit idle when the\n\tview is fullscreen (or a descendant of a fullscreen container) and is\n\tvisible. _open_ will inhibit idle until the window is closed (or the\n\tinhibitor is unset/changed). _visible_ will inhibit idle when the window is\n\tvisible on any output. _none_ will remove any existing idle inhibitor for\n\tthe window.\n\n\tThis can also be used with criteria to set an idle inhibitor for any\n\texisting window or with _for_window_ to set idle inhibitors for future windows.\n\n*layout* default|splith|splitv|stacking|tabbed\n\tSets the layout mode of the focused container.\n\n\tWhen using the _stacking_ layout, only the focused window in the container is\n\tdisplayed, with the opened windows' list on the top of the container.\n\n\tThe _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically\n\tsplit.\n\n*layout* toggle [split|all]\n\tCycles the layout mode of the focused container though a preset list of\n\tlayouts. If no argument is given, then it cycles through stacking, tabbed\n\tand the last split layout. If _split_ is given, then it cycles through\n\tsplith and splitv. If _all_ is given, then it cycles through every layout.\n\n*layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...\n\tCycles the layout mode of the focused container through a list of layouts.\n\n*max_render_time* off|<msec>\n\tControls when the relevant application is told to render this window, as a\n\tpositive number of milliseconds before the next time sway composites the\n\toutput. A smaller number leads to fresher rendered frames being composited\n\tby sway and lower perceived input latency, but if set too low, the\n\tapplication may not finish rendering before sway composites the output,\n\tleading to delayed frames.\n\n\tWhen set to off, the relevant application is told to render this window\n\timmediately after display refresh. How much time is left for rendering\n\tbefore sway composites the output at that point depends on the output\n\t*max_render_time* setting.\n\n\tTo set this up for optimal latency:\n\t. Set up *output max_render_time* (see *sway-output*(5)).\n\t. Put the target application in _full-screen_ and have it continuously\n\t  render something.\n\t. Start by setting *max_render_time 1*. If the application drops\n\t  frames, increment by *1*.\n\n\tThis setting only has an effect if a per-output *max_render_time* is in\n\teffect on the output the window is currently on. See *sway-output*(5) for\n\tfurther details.\n\n*allow_tearing* yes|no\n\tAllows or disallows screen tearing as a result of immediate page flips\n\tfor a fullscreen application.\n\n\tWhen this option is not set, the tearing hints provided by the application\n\tdetermine whether tearing is allowed. When _yes_ is specified,\n\tthe application allows tearing regardless of the tearing hints.\n\tWhen _no_ is specified, tearing will never be allowed on the application,\n\tregardless of the tearing hints.\n\n\tThis setting only has an effect if tearing is allowed on the output through\n\tthe per-output *allow_tearing* setting. See *sway-output*(5) for further\n\tdetails.\n\n*move* left|right|up|down [<px> px]\n\tMoves the focused container in the direction specified. The optional _px_\n\targument specifies how many pixels to move the container. If unspecified,\n\tthe default is 10 pixels. Pixels are ignored when moving tiled containers.\n\n*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]\n\tMoves the focused container to the specified position in the workspace.\n\tThe position can be specified in pixels or percentage points, omitting\n\tthe unit defaults to pixels. If _absolute_ is used, the position is\n\trelative to all outputs. _absolute_ can not be used with percentage points.\n\n*move* [absolute] position center\n\tMoves the focused container to be centered on the workspace. If _absolute_\n\tis used, it is moved to the center of all outputs.\n\n*move* position cursor|mouse|pointer\n\tMoves the focused container to be centered on the cursor.\n\n*move* [container|window] [to] mark <mark>\n\tMoves the focused container to the specified mark.\n\n*move* [--no-auto-back-and-forth] [container|window] [to] workspace [number] <name>\n\tMoves the focused container to the specified workspace. The string _number_\n\tis optional and is used to match a workspace with the same number, even if\n\tit has a different name.\n\n*move* [container|window] [to] workspace prev|next|current\n\tMoves the focused container to the previous, next or current workspace on\n\tthis output, or if no workspaces remain, the previous or next output.\n\n*move* [container|window] [to] workspace prev_on_output|next_on_output\n\tMoves the focused container to the previous or next workspace on this\n\toutput, wrapping around if already at the first or last workspace.\n\n*move* [container|window] [to] workspace back_and_forth\n\tMoves the focused container to previously focused workspace.\n\n*move* [container|window] [to] output <name-or-id>|current\n\tMoves the focused container to the specified output.\n\n*move* [container|window] [to] output up|right|down|left\n\tMoves the focused container to next output in the specified\n\tdirection.\n\n*move* [container|window] [to] scratchpad\n\tMoves the focused container to the scratchpad.\n\n*move* workspace [to] output <name-or-id>|current\n\tMoves the focused workspace to the specified output.\n\n*move* workspace to [output] <name-or-id>|current\n\tMoves the focused workspace to the specified output.\n\n*move* workspace [to] output up|right|down|left\n\tMoves the focused workspace to next output in the specified direction.\n\n*move* workspace to [output] up|right|down|left\n\tMoves the focused workspace to next output in the specified direction.\n\n*nop* <comment>\n\tA no operation command that can be used to override default behaviour. The\n\toptional comment argument is ignored, but logged for debugging purposes.\n\n*reload*\n\tReloads the sway config file and applies any changes. The config file is\n\tlocated at path specified by the command line arguments when started,\n\totherwise according to the priority stated in *sway*(1).\n\n*rename* workspace [<old_name>] to <new_name>\n\tRename either <old_name> or the focused workspace to the <new_name>\n\n*resize* shrink|grow up|right|down|left|width|height [<amount> [px|ppt]]\n\tResizes the currently focused container by _amount_, specified in pixels or\n\tpercentage points. If the units are omitted, floating containers are resized\n\tin px and tiled containers by ppt. _amount_ will default to 10 if omitted.\n\tFor tiling containers, space is taken/given from the container in the\n\tspecified direction. If _width_ or _height_ is specified, space will be\n\ttaken/given from all other containers.\n\n*resize* set height <height> [px|ppt]\n\tSets the height of the container to _height_, specified in pixels or\n\tpercentage points. If the units are omitted, floating containers are\n\tresized in px and tiled containers by ppt. If _height_ is 0, the container\n\twill not be resized. For tiling containers, space is taken/given from all\n\tother containers.\n\n*resize* set [width] <width> [px|ppt]\n\tSets the width of the container to _width_, specified in pixels or\n\tpercentage points. If the units are omitted, floating containers are\n\tresized in px and tiled containers by ppt. If _width_ is 0, the container\n\twill not be resized. For tiling containers, space is taken/given from all\n\tother containers.\n\n*resize* set [width] <width> [px|ppt] [height] <height> [px|ppt]\n\tSets the width and height of the container to _width_ and _height_,\n\tspecified in pixels or percentage points. If the units are omitted,\n\tfloating containers are resized in px and tiled containers by ppt. If\n\t_width_ or _height_ is 0, the container will not be resized on that axis.\n\tFor tiling containers, space is taken/given from all other containers.\n\n*scratchpad* show\n\tShows a window from the scratchpad. Repeatedly using this command will\n\tcycle through the windows in the scratchpad.\n\n*shortcuts_inhibitor* enable|disable\n\tEnables or disables the ability of clients to inhibit keyboard\n\tshortcuts for a window. This is primarily useful for virtualization and\n\tremote desktop software. It affects either the currently focused window\n\tor a set of windows selected by criteria. Subcommand _disable_\n\tadditionally deactivates any active inhibitors for the given window(s).\n\tCriteria are particularly useful with the *for_window* command to\n\tconfigure a class of windows differently from the per-seat defaults\n\testablished by the *seat* subcommand of the same name. See\n\t*sway-input*(5) for more ways to affect inhibitors.\n\n*split* vertical|v|horizontal|h|none|n|toggle|t\n\tSplits the current container, vertically or horizontally. When _none_ is\n\tspecified, the effect of a previous split is undone if the current\n\tcontainer is the only child of a split parent. When _toggle_ is\n\tspecified, the current container is split opposite to the parent\n\tcontainer's layout.\n\n*splith*\n\tEquivalent to *split horizontal*\n\n*splitv*\n\tEquivalent to *split vertical*\n\n*splitt*\n\tEquivalent to *split toggle*\n\n*sticky* enable|disable|toggle\n\t\"Sticks\" a floating window to the current output so that it shows up on all\n\tworkspaces.\n\n*swap* container with id|con_id|mark <arg>\n\tSwaps the position, geometry, and fullscreen status of two containers. The\n\tfirst container can be selected either by criteria or focus. The second\n\tcontainer can be selected by _id_, _con_id_, or _mark_. _id_ can only be\n\tused with xwayland windows. If the first container has focus, it will retain\n\tfocus unless it is moved to a different workspace or the second container\n\tbecomes fullscreen on the same workspace as the first container. In either\n\tof those cases, the second container will gain focus.\n\n*title_format* <format>\n\tSets the format of window titles. The following placeholders may be used:\n\n\t*%title*\n\t\tThe title supplied by the window\n\n\t*%app_id*\n\t\tThe wayland app ID (applicable to wayland windows only)\n\n\t*%class*\n\t\tThe X11 classname (applicable to xwayland windows only)\n\n\t*%instance*\n\t\tThe X11 instance (applicable to xwayland windows only)\n\n\t*%shell*\n\t\tThe protocol the window is using (typically xwayland or xdg_shell)\n\n\t*%sandbox_engine*\n\t\tThe associated sandbox engine\n\n\t*%sandbox_app_id*\n\t\tThe app ID provided by the associated sandbox engine\n\n\t*%sandbox_instance_id*\n\t\tThe instance ID provided by the associated sandbox engine\n\n\tThis command is typically used with *for_window* criteria. For example:\n\n\t\tfor_window [title=\".\"] title_format \"<b>%title</b> (%app_id)\"\n\n\tNote that markup requires pango to be enabled via the *font* command.\n\n\tThe default format is \"%title\".\n\n## Config or runtime commands\nThe following commands may be used either in the configuration file or at\nruntime.\n\n*assign* <criteria> [→] [workspace] [number] <workspace>\n\tAssigns windows matching _criteria_ (see *CRITERIA* for details) to\n\t_workspace_. The → (U+2192) is optional and cosmetic. This command is\n\tequivalent to:\n\n\t\tfor_window <criteria> move container to workspace <workspace>\n\n*assign* <criteria> [→] output left|right|up|down|<name>\n\tAssigns windows matching _criteria_ (see *CRITERIA* for details) to the\n\tspecified output. The → (U+2192) is optional and cosmetic. This command is\n\tequivalent to:\n\n\t\tfor_window <criteria> move container to output <output>\n\n*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \\\n[--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \\\n[Group<1-4>+]<key combo> <command>\n\tBinds _key combo_ to execute the sway command _command_ when pressed. You\n\tmay use XKB key names here (*wev*(1) is a good tool for discovering these).\n\tWith the flag _--release_, the command is executed when the key combo is\n\treleased. If _input-device_ is given, the binding will only be executed for\n\tthat input device and will be executed instead of any binding that is\n\tgeneric to all devices. If a group number is given, then the binding will\n\tonly be available for that group. By default, if you overwrite a binding,\n\tswaynag will give you a warning. To silence this, use the _--no-warn_ flag.\n\n\tFor specifying modifier keys, you can use the XKB modifier names _Shift_,\n\t_Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock),\n\t_Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for\n\tAltGr). In addition, you can use the aliases  _Ctrl_ (for Control), _Alt_\n\t(for Alt), and _Super_ (for the Logo key).\n\n\tUnless the flag _--locked_ is set, the command will not be run when a\n\tscreen locking program is active. If there is a matching binding with\n\tand without _--locked_, the one with will be preferred when locked and the\n\tone without will be preferred when unlocked. If there are matching bindings\n\tand one has both _--input-device_ and _--locked_ and the other has neither,\n\tthe former will be preferred even when unlocked.\n\n\tUnless the flag _--inhibited_ is set, the command will not be run when\n\ta keyboard shortcuts inhibitor is active for the currently focused\n\twindow. Such inhibitors are usually requested by remote desktop and\n\tvirtualization software to enable the user to send keyboard shortcuts\n\tto the remote or virtual session. The _--inhibited_ flag allows one to\n\tdefine bindings which will be exempt from pass-through to such\n\tsoftware. The same preference logic as for _--locked_ applies.\n\n\tUnless the flag _--no-repeat_ is set, the command will be run\n\trepeatedly when the key is held, according to the repeat\n\tsettings specified in the input configuration.\n\n\tBindings to keysyms are layout-dependent. This can be changed with the\n\t_--to-code_ flag. In this case, the keysyms will be translated into the\n\tcorresponding keycodes in the first configured layout.\n\n\tMouse bindings operate on the container under the cursor instead of the\n\tcontainer that has focus. Mouse buttons can either be specified in the form\n\t_button[1-9]_ or by using the name of the event code (ex _BTN\\_LEFT_ or\n\t_BTN\\_RIGHT_). For the former option, the buttons will be mapped to their\n\tvalues in X11 (1=left, 2=middle, 3=right, 4=scroll up, 5=scroll down,\n\t6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option,\n\tyou can find the event names using _libinput debug-events_.\n\n\tThe priority for matching bindings is as follows: input device, group,\n\tand locked state.\n\n\t_--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options\n\twhich affect the region in which the mouse bindings can be triggered.  By\n\tdefault, mouse bindings are only triggered when over the title bar. With the\n\t_--border_ option, the border of the window will be included in this region.\n\tWith the _--whole-window_ option, the cursor can be anywhere over a window\n\tincluding the title, border, and content. _--exclude-titlebar_ can be used in\n\tconjunction with any other option to specify that the titlebar should be\n\texcluded from the region of consideration.\n\n\tIf _--whole-window_ is given, the command can be triggered when the cursor\n\tis over an empty workspace. Using a mouse binding over a layer surface's\n\texclusive region is not currently possible.\n\n\tExample:\n```\n\t\t# Execute firefox when alt, shift, and f are pressed together\n\t\tbindsym Mod1+Shift+f exec firefox\n```\n\n\t*bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \\\n[--locked] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \\\n[Group<1-4>+]<code> <command>\n\tis also available for binding with key/button codes instead of key/button names.\n\n*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command>\n\tBinds <switch> to execute the sway command _command_ on state changes.\n\tSupported switches are _lid_ (laptop lid), _tablet_ (tablet mode) and\n\t_keypad_slide_ (whether the device keypad is exposed or not) switches. Valid\n\tvalues for _state_ are _on_, _off_ and _toggle_. These switches are on when\n\tthe device lid is shut, when tablet mode is active and when the keypad is\n\texposed respectively. _toggle_ is also supported to run a command both when\n\tthe switch is toggled on or off.\n\n\tUnless the flag _--locked_ is set, the command will not be run when a\n\tscreen locking program is active. If there is a matching binding with\n\tand without _--locked_, the one with will be preferred when locked and the\n\tone without will be preferred when unlocked.\n\n\tIf the _--reload_ flag is given, the binding will also be executed when\n\tthe config is reloaded. _toggle_ bindings will not be executed on reload.\n\tThe _--locked_ flag will operate as normal so if the config is reloaded\n\twhile locked and _--locked_ is not given, the binding will not be executed.\n\n\tBy default, if you overwrite a binding, swaynag will give you a warning. To\n\tsilence this, use the _--no-warn_ flag.\n\n\tExample:\n```\n\t\t# Show the virtual keyboard when tablet mode is entered.\n\t\tbindswitch tablet:on busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true\n\n\t\t# Log a message when the laptop lid is opened or closed.\n\t\tbindswitch lid:toggle exec echo \"Lid moved\"\n```\n\n*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \\\n<gesture>[:<fingers>][:directions] <command>\n\tBinds _gesture_ to execute the sway command _command_ when detected.\n\tCurrently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally\n\tcan be limited to bind to a certain number of _fingers_ or, for a\n\t_pinch_ or _swipe_ gesture, to certain _directions_.\n\n[[ *type*\n:[ *fingers*\n:< *direction*\n|  hold\n:- 1 - 5\n:  none\n|  swipe\n:  3 - 5\n:  up, down, left, right\n|  pinch\n:  2 - 5\n:  all above + inward, outward, clockwise, counterclockwise\n\n\tThe _fingers_ can be limited to any sensible number or left empty to accept\n\tany finger counts.\n\tValid directions are _up_, _down_, _left_ and _right_, as well as _inward_,\n\t_outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.\n\tMultiple directions can be combined by a plus.\n\n\tIf a _input-device_ is given, the binding will only be executed for\n\tthat input device and will be executed instead of any binding that is\n\tgeneric to all devices. By default, if you overwrite a binding,\n\tswaynag will give you a warning. To silence this, use the _--no-warn_ flag.\n\n\tThe _--exact_ flag can be used to ensure a binding only matches when exactly\n\tall specified directions are matched and nothing more. If there is matching\n\tbinding with _--exact_, it will be preferred.\n\n\tThe priority for matching bindings is as follows: input device, then\n\texact matches followed by matches with the highest number of matching\n\tdirections.\n\n\tGestures executed while the pointer is above a bar are not handled by sway.\n\tSee the respective documentation, e.g. *bindgesture* in *sway-bar*(5).\n\n\tExample:\n```\n\t\t# Allow switching between workspaces with left and right swipes\n\t\tbindgesture swipe:right workspace prev\n\t\tbindgesture swipe:left workspace next\n\n\t\t# Allow container movements by pinching them\n\t\tbindgesture pinch:inward+up move up\n\t\tbindgesture pinch:inward+down move down\n\t\tbindgesture pinch:inward+left move left\n\t\tbindgesture pinch:inward+right move right\n\n```\n\n*client.background* <color>\n\tThis command is ignored and is only present for i3 compatibility.\n\n*client.<class>* <border> <background> <text> [<indicator> [<child_border>]]\n\tConfigures the color of window borders and title bars. The first three\n\tcolors are required. When omitted _indicator_ will use a sane default and\n\t_child_border_ will use the color set for _background_. Colors may be\n\tspecified in hex, either as _#RRGGBB_ or _#RRGGBBAA_.\n\n\tThe available classes are:\n\n\t*client.focused*\n\t\tThe window that has focus.\n\n\t*client.focused_inactive*\n\t\tThe most recently focused window within a container which is not focused.\n\n\t*client.focused_tab_title*\n\t\tA window that has focused descendant container.\n\t\tTab or stack container title that is the parent of the focused container\n\t\tbut is not directly focused. Defaults to focused_inactive if not\n\t\tspecified and does not use the indicator and child_border colors.\n\n\t*client.placeholder*\n\t\tIgnored (present for i3 compatibility).\n\n\t*client.unfocused*\n\t\tA window that does not have focus.\n\n\t*client.urgent*\n\t\tA window with an urgency hint. *Note*: Native Wayland windows do not\n\t\tsupport urgency. Urgency only works for Xwayland windows.\n\n\tThe meaning of each color is:\n\n\t_border_\n\t\tThe border around the title bar.\n\n\t_background_\n\t\tThe background of the title bar.\n\n\t_text_\n\t\tThe text color of the title bar.\n\n\t_indicator_\n\t\tThe color used to indicate where a new window will open. In a tiled\n\t\tcontainer, this would paint the right border of the current window if a\n\t\tnew window would be opened to the right.\n\n\t_child_border_\n\t\tThe border around the window itself.\n\nThe default colors are:\n\n[- *class*\n:[ _border_\n:[ _background_\n:[ _text_\n:[ _indicator_\n:[ _child_border_\n|[ *background*\n:  n/a\n:  #ffffff\n:  n/a\n:  n/a\n:  n/a\n|  *focused*\n:  #4c7899\n:  #285577\n:  #ffffff\n:  #2e9ef4\n:  #285577\n|  *focused_inactive*\n:  #333333\n:  #5f676a\n:  #ffffff\n:  #484e50\n:  #5f676a\n|  *focused_tab_title*\n:  #333333\n:  #5f676a\n:  #ffffff\n:  n/a\n:  n/a\n|  *unfocused*\n:  #333333\n:  #222222\n:  #888888\n:  #292d2e\n:  #222222\n|  *urgent*\n:  #2f343a\n:  #900000\n:  #ffffff\n:  #900000\n:  #900000\n|  *placeholder*\n:  #000000\n:  #0c0c0c\n:  #ffffff\n:  #000000\n:  #0c0c0c\n\n\n*default_border* normal|none|pixel [<n>]\n\tSet default border style for new tiled windows. Config reload won't affect\n\texisting windows, only newly created ones after the reload.\n\n*default_floating_border* normal|none|pixel [<n>]\n\tSet default border style for new floating windows. This only applies to\n\twindows that are spawned in floating mode, not windows that become floating\n\tafterwards.\n\n*exec* <shell command>\n\tExecutes _shell command_ with sh.\n\n*exec_always* <shell command>\n\tLike *exec*, but the shell command will be executed _again_ after *reload*.\n\n*floating_maximum_size* <width> x <height>\n\tSpecifies the maximum size of floating windows. -1 x -1 removes the upper\n\tlimit. The default is 0 x 0, which will use the width and height of the\n\tentire output layout as the maximums\n\n*floating_minimum_size* <width> x <height>\n\tSpecifies the minimum size of floating windows. The default is 75 x 50.\n\n*floating_modifier* <modifier> [normal|inverse]\n\tWhen the _modifier_ key is held down, you may hold left click to move\n\twindows, and right click to resize them. Setting _modifier_ to _none_\n\tdisables this feature. If _inverse_ is specified, left click is used for\n\tresizing and right click for moving.\n\n*focus_follows_mouse* yes|no|always\n\tIf set to _yes_, moving your mouse over a window will focus that window. If\n\tset to _always_, the window under the cursor will always be focused, even\n\tafter switching between workspaces.\n\n*focus_on_window_activation* smart|urgent|focus|none\n\tThis option determines what to do when a client requests window activation.\n\tIf set to _urgent_, the urgent state will be set for that window. If set to\n\t_focus_, the window will become focused. If set to _smart_, the window will\n\tbecome focused only if it is already visible, otherwise the urgent state\n\twill be set. Default is _urgent_.\n\n*focus_wrapping* yes|no|force|workspace\n\tThis option determines what to do when attempting to focus over the edge\n\tof a container. If set to _no_, the focused container will retain focus,\n\tif there are no other containers in the direction. If set to _yes_, focus\n\twill be wrapped to the opposite edge of the container, if there are no\n\tother containers in the direction. If set to _force_, focus will be wrapped\n\tto the opposite edge of the container, even if there are other containers\n\tin the direction. If set to _workspace_, focus will wrap like in the _yes_\n\tcase and additionally wrap when moving outside of workspaces boundaries.\n\tDefault is _yes_.\n\n*font* [pango:]<font>\n\tSets font to use for the title bars. To enable support for pango markup,\n\tpreface the font name with _pango:_. For example, _monospace 10_ is the\n\tdefault font. To enable support for pango markup, _pango:monospace 10_\n\tshould be used instead. Regardless of whether pango markup is enabled,\n\t_font_ should be specified as a pango font description. For more\n\tinformation on pango font descriptions, see\n\thttps://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description\n\n*force_display_urgency_hint* <timeout> [ms]\n\tIf an application on another workspace sets an urgency hint, switching to this\n\tworkspace may lead to immediate focus of the application, which also means the\n\twindow decoration color would be immediately reset to *client.focused*. This\n\tmay make it unnecessarily hard to tell which window originally raised the\n\tevent. This option allows one to set a _timeout_ in ms to delay the urgency hint reset.\n\n*titlebar_border_thickness* <thickness>\n\tThickness of the titlebar border in pixels\n\n*titlebar_padding* <horizontal> [<vertical>]\n\tPadding of the text in the titlebar. _horizontal_ value affects horizontal\n\tpadding of the text while _vertical_ value affects vertical padding (space\n\tabove and below text). Padding includes titlebar borders so their value\n\tshould be greater than titlebar_border_thickness. If _vertical_ value is\n\tnot specified it is set to the _horizontal_ value.\n\n*for_window* <criteria> <command>\n\tWhenever a window that matches _criteria_ appears, run list of commands.\n\tSee *CRITERIA* for more details.\n\n*gaps* inner|outer|horizontal|vertical|top|right|bottom|left <amount>\n\tSets default _amount_ pixels of _inner_ or _outer_ gap, where the inner\n\taffects spacing around each window and outer affects the spacing around each\n\tworkspace. Outer gaps are in addition to inner gaps. To reduce or remove\n\touter gaps, outer gaps can be set to a negative value. _outer_ gaps can\n\talso be specified per side with _top_, _right_, _bottom_, and _left_ or\n\tper direction with _horizontal_ and _vertical_.\n\n\tThis affects new workspaces only, and is used when the workspace doesn't\n\thave its own gaps settings (see: workspace <ws> gaps ...).\n\n*hide_edge_borders* [--i3] none|vertical|horizontal|both|smart|smart_no_gaps\n\tHides window borders adjacent to the screen edges. Default is _none_. The\n\t_--i3_ option enables i3-compatible behavior to hide the title bar on\n\ttabbed and stacked containers with one child. The _smart_|_smart_no_gaps_\n\toptions are equivalent to setting _smart_borders_ smart|no_gaps and\n\t_hide_edge_borders_ none.\n\n*input* <input_device> <input-subcommands...>\n\tFor details on input subcommands, see *sway-input*(5).\n\n\t\\* may be used in lieu of a specific device name to configure all input\n\tdevices. A list of input device names may be obtained via *swaymsg -t\n\tget_inputs*.\n\n*seat* <seat> <seat-subcommands...>\n\tFor details on seat subcommands, see *sway-input*(5).\n\n*kill*\n\tKills (closes) the currently focused container and all of its children.\n\n*smart_borders* on|no_gaps|off\n\tIf smart_borders are _on_, borders will only be enabled if the workspace\n\thas more than one visible child. If smart_borders is set to _no_gaps_,\n\tborders will only be enabled if the workspace has more than one visible\n\tchild and gaps equal to zero.\n\n*smart_gaps* on|off|toggle|inverse_outer\n\tIf smart_gaps are _on_ gaps will only be enabled if a workspace has more\n\tthan one child. If smart_gaps are _inverse_outer_ outer gaps will only\n\tbe enabled if a workspace has exactly one child.\n\n*mark* --add|--replace [--toggle] <identifier>\n\tMarks are arbitrary labels that can be used to identify certain windows and\n\tthen jump to them at a later time. Each _identifier_ can only be set on a\n\tsingle window at a time since they act as a unique identifier. By default,\n\t*mark* sets _identifier_ as the only mark on a window. _--add_ will instead\n\tadd _identifier_ to the list of current marks for that window. If _--toggle_\n\tis specified mark will remove _identifier_ if it is already marked.\n\n*mode* <mode>\n\tSwitches to the specified mode. The default mode is _default_.\n\n*mode* [--pango_markup] <mode> <mode-subcommands...>\n\tThe only valid _mode-subcommands..._ are *bindsym*, *bindcode*,\n\t*bindswitch*, and *set*. If _--pango_markup_ is given, then _mode_ will be\n\tinterpreted as pango markup.\n\n*mouse_warping* output|container|none\n\tIf _output_ is specified, the mouse will be moved to new outputs as you\n\tmove focus between them. If _container_ is specified, the mouse will be\n\tmoved to the middle of the container on switch. Default is _output_.\n\n*no_focus* <criteria>\n\tPrevents windows matching <criteria> from being focused automatically when\n\tthey're created. This has no effect on the first window in a workspace.\n\n*output* <output_name> <output-subcommands...>\n\tFor details on output subcommands, see *sway-output*(5).\n\n\t\\* may be used in lieu of a specific output name to configure all outputs.\n\tA list of output names may be obtained via *swaymsg -t get_outputs*.\n\n*popup_during_fullscreen* smart|ignore|leave_fullscreen\n\tDetermines what to do when a fullscreen window opens a dialog.\n\tIf _smart_ (the default), the dialog will be displayed. If _ignore_, the\n\tdialog will not be rendered. If _leave_fullscreen_, the window will exit\n\tfullscreen mode and the dialog will be rendered.\n\n*primary_selection* enabled|disabled\n\tEnable or disable the primary selection clipboard. May only be configured\n\tat launch. Default is _enabled_.\n\n*set* $<name> <value>\n\tSets variable $_name_ to _value_. You can use the new variable in the\n\targuments of future commands. When the variable is used, it can be escaped\n\twith an additional $ (ie $$_name_) to have the replacement happen at run\n\ttime instead of when reading the config. However, it does not always make\n\tsense for the variable to be replaced at run time since some arguments do\n\tneed to be known at config time.\n\n*show_marks* yes|no\n\tIf *show_marks* is yes, marks will be displayed in the window borders.\n\tAny mark that starts with an underscore will not be drawn even if\n\t*show_marks* is yes. The default is _yes_.\n\n*opacity* [set|plus|minus] <value>\n\tAdjusts the opacity of the window between 0 (completely transparent) and\n\t1 (completely opaque). If the operation is omitted, _set_ will be used.\n\n*tiling_drag*  enable|disable|toggle\n\tSets whether or not tiling containers can be dragged with the mouse. If\n\t_enabled_ (default), the _floating_mod_ can be used to drag tiling, as well\n\tas floating, containers. Using the left mouse button on title bars without\n\tthe _floating_mod_ will also allow the container to be dragged. _toggle_\n\tshould not be used in the config file.\n\n*tiling_drag_threshold* <threshold>\n\tSets the threshold that must be exceeded for a container to be dragged by\n\tits titlebar. This has no effect if _floating_mod_ is used or if\n\t_tiling_drag_ is set to _disable_.  Once the threshold has been exceeded\n\tonce, the drag starts and the cursor can come back inside the threshold\n\twithout stopping the drag.  _threshold_ is multiplied by the scale of the\n\toutput that the cursor on.  The default is 9.\n\n*title_align* left|center|right\n\tSets the title alignment. If _right_ is selected and _show_marks_ is set\n\tto _yes_, the marks will be shown on the _left_ side instead of the\n\t_right_ side.\n\n*unbindswitch* <switch>:<state>\n\tRemoves a binding for when <switch> changes to <state>.\n\n*unbindgesture* [--exact] [--input-device=<device>] \\\n<gesture>[:<fingers>][:directions]\n\tRemoves a binding for the specified _gesture_, _fingers_\n\tand _directions_ combination.\n\n*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \\\n[--to-code] [--input-device=<device>] <key combo>\n\tRemoves the binding for _key combo_ that was previously bound with the\n\tgiven flags.  If _input-device_ is given, only the binding for that\n\tinput device will be unbound.\n\n\t*unbindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \\\n[--locked] [--input-device=<device>] <code>\n\tis also available for unbinding with key/button codes instead of key/button names.\n\n*unmark* [<identifier>]\n\t*unmark* will remove _identifier_ from the list of current marks on a\n\twindow. If _identifier_ is omitted, all marks are removed.\n\n*urgent* enable|disable|allow|deny\n\tUsing _enable_ or _disable_ manually sets or unsets the window's urgent\n\tstate. Using _allow_ or _deny_ controls the window's ability to set itself\n\tas urgent. By default, windows are allowed to set their own urgency.\n\n*workspace* [--no-auto-back-and-forth] [number] <[num:]name>\n\tSwitches to the specified workspace. The _num:_ portion of the name is\n\toptional and will be used for ordering. If _num:_ is not given and\n\t_name_ is a number, then it will be also be used for ordering.\n\n\tIf the _no-auto-back-and-forth_ option is given, then this command will\n\tnot perform a back-and-forth operation when the workspace is already\n\tfocused and _workspace_auto_back_and_forth_ is enabled.\n\n\tIf the _number_ keyword is specified and a workspace with the number\n\talready exists, then the workspace with the number will be used. If a\n\tworkspace with the number does not exist, a new workspace will be created\n\twith the name _name_.\n\n*workspace* prev|next\n\tSwitches to the next workspace on the current output or on the next output\n\tif currently on the last workspace.\n\n*workspace* prev_on_output|next_on_output\n\tSwitches to the next workspace on the current output.\n\n*workspace* back_and_forth\n\tSwitches to the previously focused workspace.\n\n*workspace* <name> gaps inner|outer|horizontal|vertical|top|right|bottom|left\n<amount>\n\tSpecifies that workspace _name_ should have the given gaps settings when it\n\tis created.\n\n\tThis command does not affect existing workspaces. To alter the gaps of an\n\texisting workspace, use the _gaps_ command.\n\n*workspace* <name> output <outputs...>\n\tSpecifies that workspace _name_ should be shown on the specified _outputs_.\n\tMultiple outputs can be listed and the first available will be used. If the\n\tworkspace gets placed on an output further down the list and an output that\n\tis higher on the list becomes available, the workspace will be moved to the\n\thigher priority output.\n\n\tThis command does not affect existing workspaces. To move an existing\n\tworkspace, use the _move_ command in combination with the _workspace_\n\tcriteria (non-empty workspaces only) or _workspace_ command (to switch\n\tto the workspace before moving).\n\n*workspace_auto_back_and_forth* yes|no\n\tWhen _yes_, repeating a workspace switch command will switch back to the\n\tprior workspace. For example, if you are currently on workspace 1,\n\tswitch to workspace 2, then invoke the *workspace 2* command again, you\n\twill be returned to workspace 1. Default is _no_.\n\n# CRITERIA\n\nA criteria is a string in the form of, for example:\n\n```\n[app_id=\"some-application\" title=\"[Rr]egex.*\"]\n```\n\nThe string contains one or more (space separated) attribute/value pairs. They\nare used by some commands to choose which windows to execute actions on. All\nattributes must match for the criteria to match. Criteria is retained across\ncommands separated by a *,*, but will be reset (and allow for new criteria, if\ndesired) for commands separated by a *;*.\n\nCriteria may be used with either the *for_window* or *assign* commands to\nspecify operations to perform on new windows. A criteria may also be used to\nperform specific commands (ones that normally act upon one window) on all windows\nthat match that criteria. For example:\n\nFocus on a window with the mark \"IRC\":\n\n```\n[con_mark=\"IRC\"] focus\n```\n\nKill all windows where the title contains \"Emacs\":\n\n```\n[title=\"Emacs\"] kill\n```\n\nSeveral attributes allow regular expressions. These use Perl-compatible regular\nexpressions (PCRE2), which are documented in *pcre2pattern*(3) and summarized in\n*pcre2syntax*(3). For example, this moves all windows with titles ending in\n\"sway\" or \"Sway\" to workspace 1:\n\n```\n[title=\"[Ss]way$\"] move workspace 1\n```\n\nYou may like to use swaymsg -t get_tree for finding the values of these\nproperties in practice for your applications.\n\nThe following attributes may be matched with:\n\n*all*\n\tMatches all windows.\n\n*app_id*\n\tCompare value against the app id. Can be a regular expression. If value is\n\t\\_\\_focused\\_\\_, then the app id must be the same as that of the currently\n\tfocused window. _app_id_ are specific to Wayland applications.\n\n*class*\n\tCompare value against the window class. Can be a regular expression. If\n\tvalue is \\_\\_focused\\_\\_, then the window class must be the same as that of\n\tthe currently focused window. _class_ are specific to X11 applications and\n\trequire XWayland.\n\n*con_id*\n\tCompare against the internal container ID, which you can find via IPC. If\n\tvalue is \\_\\_focused\\_\\_, then the id must be the same as that of the\n\tcurrently focused window.\n\n*con_mark*\n\tCompare against the window marks. Can be a regular expression.\n\n*floating*\n\tMatches floating windows.\n\n*id*\n\tCompare value against the X11 window ID. Must be numeric. id is specific to\n\tX11 applications and requires XWayland.\n\n*instance*\n\tCompare value against the window instance. Can be a regular expression. If\n\tvalue is \\_\\_focused\\_\\_, then the window instance must be the same as that\n\tof the currently focused window. instance is specific to X11 applications and\n\trequires XWayland.\n\n*pid*\n\tCompare value against the window's process ID. Must be numeric.\n\n*shell*\n\tCompare value against the window shell, such as \"xdg_shell\" or \"xwayland\".\n\tCan be a regular expression. If value is \\_\\_focused\\_\\_, then the shell\n\tmust be the same as that of the currently focused window.\n\n*tag*\n\tCompare value against the tag. _tag_ is specific to Wayland applications.\n\n*tiling*\n\tMatches tiling windows.\n\n*title*\n\tCompare against the window title. Can be a regular expression. If value is\n\t\\_\\_focused\\_\\_, then the window title must be the same as that of the\n\tcurrently focused window.\n\n*urgent*\n\tCompares the urgent state of the window. Can be _first_, _last_, _latest_,\n\t_newest_, _oldest_ or _recent_.\n\n*window_role*\n\tCompare against the window role (WM_WINDOW_ROLE). Can be a regular\n\texpression. If value is \\_\\_focused\\_\\_, then the window role must be the\n\tsame as that of the currently focused window. window_role is specific to X11\n\tapplications and requires XWayland.\n\n*window_type*\n\tCompare against the window type (\\_NET_WM_WINDOW_TYPE). Possible values\n\tare normal, dialog, utility, toolbar, splash, menu, dropdown_menu,\n\tpopup_menu, tooltip and notification. window_type is specific to X11\n\tapplications and requires XWayland.\n\n*workspace*\n\tCompare against the workspace name for this window. Can be a regular\n\texpression. If the value is \\_\\_focused\\_\\_, then all the windows on the\n\tcurrently focused workspace matches.\n\n*sandbox_engine*\n\tCompare against the associated sandbox engine. Can be a regular expression.\n\tIf the value is \\_\\_focused\\_\\_, then the sandbox engine must be the same as\n\tthat of the currently focused window.\n\n*sandbox_app_id*\n\tCompare against the app ID provided by the associated sandbox engine. Can be\n\ta regular expression. If the value is \\_\\_focused\\_\\_, then the sandbox app\n\tID must be the same as that of the currently focused window.\n\n*sandbox_instance_id*\n\tCompare against the instance ID provided by the associated sandbox engine.\n\tCan be a regular expression. If the value is \\_\\_focused\\_\\_, then the\n\tsandbox instance ID must be the same as that of the currently focused\n\twindow.\n\n# SEE ALSO\n\n*sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7)\n*pcre2pattern*(3) *pcre2syntax*(3)\n"
  },
  {
    "path": "sway/sway_text_node.c",
    "content": "#include <drm_fourcc.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <wlr/types/wlr_buffer.h>\n#include <wlr/interfaces/wlr_buffer.h>\n#include \"cairo_util.h\"\n#include \"log.h\"\n#include \"pango.h\"\n#include \"sway/config.h\"\n#include \"sway/sway_text_node.h\"\n\nstruct cairo_buffer {\n\tstruct wlr_buffer base;\n\tcairo_surface_t *surface;\n\tcairo_t *cairo;\n};\n\nstatic void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) {\n\tstruct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);\n\n\tcairo_surface_destroy(buffer->surface);\n\tcairo_destroy(buffer->cairo);\n\tfree(buffer);\n}\n\nstatic bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer,\n\t\tuint32_t flags, void **data, uint32_t *format, size_t *stride) {\n\tstruct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);\n\t*data = cairo_image_surface_get_data(buffer->surface);\n\t*stride = cairo_image_surface_get_stride(buffer->surface);\n\t*format = DRM_FORMAT_ARGB8888;\n\treturn true;\n}\n\nstatic void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {\n\t// This space is intentionally left blank\n}\n\nstatic const struct wlr_buffer_impl cairo_buffer_impl = {\n\t.destroy = cairo_buffer_handle_destroy,\n\t.begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access,\n\t.end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access,\n};\n\nstruct text_buffer {\n\tstruct wlr_scene_buffer *buffer_node;\n\tchar *text;\n\tstruct sway_text_node props;\n\n\tbool visible;\n\tfloat scale;\n\tenum wl_output_subpixel subpixel;\n\n\tstruct wl_listener outputs_update;\n\tstruct wl_listener destroy;\n};\n\nstatic int get_text_width(struct sway_text_node *props) {\n\tint width = props->width;\n\tif (props->max_width >= 0) {\n\t\twidth = MIN(width, props->max_width);\n\t}\n\treturn MAX(width, 0);\n}\n\nstatic void render_backing_buffer(struct text_buffer *buffer) {\n\tif (!buffer->visible) {\n\t\treturn;\n\t}\n\n\tif (buffer->props.max_width == 0) {\n\t\twlr_scene_buffer_set_buffer(buffer->buffer_node, NULL);\n\t\treturn;\n\t}\n\n\tfloat scale = buffer->scale;\n\tint width = ceil(get_text_width(&buffer->props) * scale);\n\tint height = ceil(buffer->props.height * scale);\n\tfloat *color = (float *)&buffer->props.color;\n\tfloat *background = (float *)&buffer->props.background;\n\tPangoContext *pango = NULL;\n\n\tcairo_font_options_t *fo = cairo_font_options_create();\n\tcairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);\n\tenum wl_output_subpixel subpixel = buffer->subpixel;\n\tif (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) {\n\t\tcairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);\n\t} else {\n\t\tcairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);\n\t\tcairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel));\n\t}\n\n\tcairo_surface_t *surface = cairo_image_surface_create(\n\t\t\tCAIRO_FORMAT_ARGB32, width, height);\n\tcairo_status_t status = cairo_surface_status(surface);\n\tif (status != CAIRO_STATUS_SUCCESS) {\n\t\tsway_log(SWAY_ERROR, \"cairo_image_surface_create failed: %s\",\n\t\t\tcairo_status_to_string(status));\n\t\tgoto err;\n\t}\n\n\tstruct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer));\n\tif (!cairo_buffer) {\n\t\tsway_log(SWAY_ERROR, \"cairo_buffer allocation failed\");\n\t\tgoto err;\n\t}\n\n\tcairo_t *cairo = cairo_create(surface);\n\tif (!cairo) {\n\t\tsway_log(SWAY_ERROR, \"cairo_create failed\");\n\t\tfree(cairo_buffer);\n\t\tgoto err;\n\t}\n\n\tcairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);\n\tcairo_set_font_options(cairo, fo);\n\tpango = pango_cairo_create_context(cairo);\n\n\tcairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]);\n\tcairo_rectangle(cairo, 0, 0, width, height);\n\tcairo_fill(cairo);\n\n\tcairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]);\n\tcairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale);\n\n\trender_text(cairo, config->font_description, scale, buffer->props.pango_markup,\n\t\t\"%s\", buffer->text);\n\n\tcairo_surface_flush(surface);\n\n\twlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height);\n\tcairo_buffer->surface = surface;\n\tcairo_buffer->cairo = cairo;\n\n\twlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base);\n\twlr_buffer_drop(&cairo_buffer->base);\n\n\tpixman_region32_t opaque;\n\tpixman_region32_init(&opaque);\n\tif (background[3] == 1) {\n\t\tpixman_region32_union_rect(&opaque, &opaque, 0, 0,\n\t\t\tget_text_width(&buffer->props), buffer->props.height);\n\t}\n\twlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque);\n\tpixman_region32_fini(&opaque);\n\nerr:\n\tif (pango) g_object_unref(pango);\n\tcairo_font_options_destroy(fo);\n}\n\nstatic void handle_outputs_update(struct wl_listener *listener, void *data) {\n\tstruct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update);\n\tstruct wlr_scene_outputs_update_event *event = data;\n\n\tfloat scale = 0;\n\tenum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;\n\n\tfor (size_t i = 0; i < event->size; i++) {\n\t\tstruct wlr_scene_output *output = event->active[i];\n\t\tif (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) {\n\t\t\tsubpixel = output->output->subpixel;\n\t\t} else if (subpixel != output->output->subpixel) {\n\t\t\tsubpixel = WL_OUTPUT_SUBPIXEL_NONE;\n\t\t}\n\n\t\tif (scale != 0 && scale != output->output->scale) {\n\t\t\t// drop down to gray scale if we encounter outputs with different\n\t\t\t// scales or else we will have chromatic aberations\n\t\t\tsubpixel = WL_OUTPUT_SUBPIXEL_NONE;\n\t\t}\n\n\t\tif (scale < output->output->scale) {\n\t\t\tscale = output->output->scale;\n\t\t}\n\t}\n\n\tbuffer->visible = event->size > 0;\n\n\tif (scale != buffer->scale || subpixel != buffer->subpixel) {\n\t\tbuffer->scale = scale;\n\t\tbuffer->subpixel = subpixel;\n\t\trender_backing_buffer(buffer);\n\t}\n}\n\nstatic void handle_destroy(struct wl_listener *listener, void *data) {\n\tstruct text_buffer *buffer = wl_container_of(listener, buffer, destroy);\n\n\twl_list_remove(&buffer->outputs_update.link);\n\twl_list_remove(&buffer->destroy.link);\n\n\tfree(buffer->text);\n\tfree(buffer);\n}\n\nstatic void text_calc_size(struct text_buffer *buffer) {\n\tstruct sway_text_node *props = &buffer->props;\n\n\tcairo_t *c = cairo_create(NULL);\n\tif (!c) {\n\t\tsway_log(SWAY_ERROR, \"cairo_t allocation failed\");\n\t\treturn;\n\t}\n\n\tcairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);\n\tget_text_size(c, config->font_description, &props->width, NULL,\n\t\t&props->baseline, 1, props->pango_markup, \"%s\", buffer->text);\n\tcairo_destroy(c);\n\n\twlr_scene_buffer_set_dest_size(buffer->buffer_node,\n\t\tget_text_width(props), props->height);\n}\n\nstruct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,\n\t\tchar *text, float color[4], bool pango_markup) {\n\tstruct text_buffer *buffer = calloc(1, sizeof(*buffer));\n\tif (!buffer) {\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL);\n\tif (!node) {\n\t\tfree(buffer);\n\t\treturn NULL;\n\t}\n\n\tbuffer->buffer_node = node;\n\tbuffer->props.node = &node->node;\n\tbuffer->props.max_width = -1;\n\tbuffer->text = strdup(text);\n\tif (!buffer->text) {\n\t\tfree(buffer);\n\t\twlr_scene_node_destroy(&node->node);\n\t\treturn NULL;\n\t}\n\n\tbuffer->props.height = config->font_height;\n\tbuffer->props.pango_markup = pango_markup;\n\tmemcpy(&buffer->props.color, color, sizeof(*color) * 4);\n\n\tbuffer->destroy.notify = handle_destroy;\n\twl_signal_add(&node->node.events.destroy, &buffer->destroy);\n\tbuffer->outputs_update.notify = handle_outputs_update;\n\twl_signal_add(&node->events.outputs_update, &buffer->outputs_update);\n\n\ttext_calc_size(buffer);\n\n\treturn &buffer->props;\n}\n\nvoid sway_text_node_set_color(struct sway_text_node *node, float color[4]) {\n\tif (memcmp(&node->color, color, sizeof(*color) * 4) == 0) {\n\t\treturn;\n\t}\n\n\tmemcpy(&node->color, color, sizeof(*color) * 4);\n\tstruct text_buffer *buffer = wl_container_of(node, buffer, props);\n\n\trender_backing_buffer(buffer);\n}\n\nvoid sway_text_node_set_text(struct sway_text_node *node, char *text) {\n\tstruct text_buffer *buffer = wl_container_of(node, buffer, props);\n\tif (strcmp(buffer->text, text) == 0) {\n\t\treturn;\n\t}\n\n\tchar *new_text = strdup(text);\n\tif (!new_text) {\n\t\treturn;\n\t}\n\n\tfree(buffer->text);\n\tbuffer->text = new_text;\n\n\ttext_calc_size(buffer);\n\trender_backing_buffer(buffer);\n}\n\nvoid sway_text_node_set_max_width(struct sway_text_node *node, int max_width) {\n\tstruct text_buffer *buffer = wl_container_of(node, buffer, props);\n\tif (max_width == buffer->props.max_width) {\n\t\treturn;\n\t}\n\tbuffer->props.max_width = max_width;\n\twlr_scene_buffer_set_dest_size(buffer->buffer_node,\n\t\tget_text_width(&buffer->props), buffer->props.height);\n\trender_backing_buffer(buffer);\n}\n\nvoid sway_text_node_set_background(struct sway_text_node *node, float background[4]) {\n\tstruct text_buffer *buffer = wl_container_of(node, buffer, props);\n\tif (memcmp(&node->background, background, sizeof(*background) * 4) == 0) {\n\t\treturn;\n\t}\n\tmemcpy(&node->background, background, sizeof(*background) * 4);\n\trender_backing_buffer(buffer);\n}\n"
  },
  {
    "path": "sway/swaynag.c",
    "content": "#include <signal.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include \"log.h\"\n#include \"sway/server.h\"\n#include \"sway/swaynag.h\"\n#include \"util.h\"\n\nstatic void handle_swaynag_client_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct swaynag_instance *swaynag =\n\t\twl_container_of(listener, swaynag, client_destroy);\n\twl_list_remove(&swaynag->client_destroy.link);\n\twl_list_init(&swaynag->client_destroy.link);\n\tswaynag->client = NULL;\n}\n\nbool swaynag_spawn(const char *swaynag_command,\n\t\tstruct swaynag_instance *swaynag) {\n\tif (swaynag->client != NULL) {\n\t\twl_client_destroy(swaynag->client);\n\t}\n\n\tif (!swaynag_command) {\n\t\treturn true;\n\t}\n\n\tif (swaynag->detailed) {\n\t\tif (pipe(swaynag->fd) != 0) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to create pipe for swaynag\");\n\t\t\treturn false;\n\t\t}\n\t\tif (!sway_set_cloexec(swaynag->fd[1], true)) {\n\t\t\tgoto failed;\n\t\t}\n\t}\n\n\tint sockets[2];\n\tif (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"socketpair failed\");\n\t\tgoto failed;\n\t}\n\tif (!sway_set_cloexec(sockets[0], true) || !sway_set_cloexec(sockets[1], true)) {\n\t\tgoto failed;\n\t}\n\n\tswaynag->client = wl_client_create(server.wl_display, sockets[0]);\n\tif (swaynag->client == NULL) {\n\t\tsway_log_errno(SWAY_ERROR, \"wl_client_create failed\");\n\t\tgoto failed;\n\t}\n\n\tswaynag->client_destroy.notify = handle_swaynag_client_destroy;\n\twl_client_add_destroy_listener(swaynag->client, &swaynag->client_destroy);\n\n\tpid_t pid = fork();\n\tif (pid < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create fork for swaynag\");\n\t\tgoto failed;\n\t} else if (pid == 0) {\n\t\tif (!sway_set_cloexec(sockets[1], false)) {\n\t\t\t_exit(EXIT_FAILURE);\n\t\t}\n\n\t\tif (swaynag->detailed) {\n\t\t\tclose(swaynag->fd[1]);\n\t\t\tdup2(swaynag->fd[0], STDIN_FILENO);\n\t\t\tclose(swaynag->fd[0]);\n\t\t}\n\n\t\tchar wayland_socket_str[16];\n\t\tsnprintf(wayland_socket_str, sizeof(wayland_socket_str),\n\t\t\t\t\"%d\", sockets[1]);\n\t\tsetenv(\"WAYLAND_SOCKET\", wayland_socket_str, true);\n\n\t\tsize_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2;\n\t\tchar *cmd = malloc(length);\n\t\tsnprintf(cmd, length, \"%s %s\", swaynag_command, swaynag->args);\n\t\texeclp(\"sh\", \"sh\", \"-c\", cmd, NULL);\n\t\tsway_log_errno(SWAY_ERROR, \"execlp failed\");\n\t\t_exit(EXIT_FAILURE);\n\t}\n\n\tif (swaynag->detailed) {\n\t\tif (close(swaynag->fd[0]) != 0) {\n\t\t\tsway_log_errno(SWAY_ERROR, \"close failed\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (close(sockets[1]) != 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"close failed\");\n\t\treturn false;\n\t}\n\n\treturn true;\n\nfailed:\n\tif (swaynag->detailed) {\n\t\tif (close(swaynag->fd[0]) != 0) {\n\t\t\tsway_log_errno(SWAY_ERROR, \"close failed\");\n\t\t\treturn false;\n\t\t}\n\t\tif (close(swaynag->fd[1]) != 0) {\n\t\t\tsway_log_errno(SWAY_ERROR, \"close failed\");\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,\n\t\tconst char *fmt, ...) {\n\tif (!swaynag_command) {\n\t\treturn;\n\t}\n\n\tif (!swaynag->detailed) {\n\t\tsway_log(SWAY_ERROR, \"Attempting to write to non-detailed swaynag inst\");\n\t\treturn;\n\t}\n\n\tif (swaynag->client == NULL && !swaynag_spawn(swaynag_command, swaynag)) {\n\t\treturn;\n\t}\n\n\tva_list args;\n\tva_start(args, fmt);\n\tchar *str = vformat_str(fmt, args);\n\tva_end(args);\n\tif (!str) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate buffer for swaynag log entry.\");\n\t\treturn;\n\t}\n\n\twrite(swaynag->fd[1], str, strlen(str));\n\n\tfree(str);\n}\n\nvoid swaynag_show(struct swaynag_instance *swaynag) {\n\tif (swaynag->detailed && swaynag->client != NULL) {\n\t\tclose(swaynag->fd[1]);\n\t}\n}\n"
  },
  {
    "path": "sway/tree/arrange.c",
    "content": "#include <ctype.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_output_layout.h>\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/tree/view.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nstatic void apply_horiz_layout(list_t *children, struct wlr_box *parent) {\n\tif (!children->length) {\n\t\treturn;\n\t}\n\n\t// Count the number of new windows we are resizing, and how much space\n\t// is currently occupied\n\tint new_children = 0;\n\tdouble current_width_fraction = 0;\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tcurrent_width_fraction += child->width_fraction;\n\t\tif (child->width_fraction <= 0) {\n\t\t\tnew_children += 1;\n\t\t}\n\t}\n\n\t// Calculate each width fraction\n\tdouble total_width_fraction = 0;\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tif (child->width_fraction <= 0) {\n\t\t\tif (current_width_fraction <= 0) {\n\t\t\t\tchild->width_fraction = 1.0;\n\t\t\t} else if (children->length > new_children) {\n\t\t\t\tchild->width_fraction = current_width_fraction /\n\t\t\t\t\t(children->length - new_children);\n\t\t\t} else {\n\t\t\t\tchild->width_fraction = current_width_fraction;\n\t\t\t}\n\t\t}\n\t\ttotal_width_fraction += child->width_fraction;\n\t}\n\t// Normalize width fractions so the sum is 1.0\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tchild->width_fraction /= total_width_fraction;\n\t}\n\n\t// Calculate gap size\n\tdouble inner_gap = 0;\n\tstruct sway_container *child = children->items[0];\n\tstruct sway_workspace *ws = child->pending.workspace;\n\tif (ws) {\n\t\tinner_gap = ws->gaps_inner;\n\t}\n\t// Descendants of tabbed/stacked containers don't have gaps\n\tstruct sway_container *temp = child;\n\twhile (temp) {\n\t\tenum sway_container_layout layout = container_parent_layout(temp);\n\t\tif (layout == L_TABBED || layout == L_STACKED) {\n\t\t\tinner_gap = 0;\n\t\t}\n\t\ttemp = temp->pending.parent;\n\t}\n\tdouble total_gap = fmin(inner_gap * (children->length - 1),\n\t\tfmax(0, parent->width - MIN_SANE_W * children->length));\n\tdouble child_total_width = parent->width - total_gap;\n\tinner_gap = floor(total_gap / (children->length - 1));\n\n\t// Resize windows\n\tsway_log(SWAY_DEBUG, \"Arranging %p horizontally\", parent);\n\tdouble child_x = parent->x;\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tchild->child_total_width = child_total_width;\n\t\tchild->pending.x = child_x;\n\t\tchild->pending.y = parent->y;\n\t\tchild->pending.width = round(child->width_fraction * child_total_width);\n\t\tchild->pending.height = parent->height;\n\n\t\t// Make last child use remaining width of parent\n\t\tif (i == children->length - 1) {\n\t\t\tchild->pending.width = parent->x + parent->width - child->pending.x;\n\t\t}\n\n\t\t// Arbitrary lower bound for window size\n\t\tif (child->pending.width < 10 || child->pending.height < 10) {\n\t\t\tchild->pending.width = 0;\n\t\t\tchild->pending.height = 0;\n\t\t}\n\t\tchild_x += child->pending.width + inner_gap;\n\t}\n}\n\nstatic void apply_vert_layout(list_t *children, struct wlr_box *parent) {\n\tif (!children->length) {\n\t\treturn;\n\t}\n\n\t// Count the number of new windows we are resizing, and how much space\n\t// is currently occupied\n\tint new_children = 0;\n\tdouble current_height_fraction = 0;\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tcurrent_height_fraction += child->height_fraction;\n\t\tif (child->height_fraction <= 0) {\n\t\t\tnew_children += 1;\n\t\t}\n\t}\n\n\t// Calculate each height fraction\n\tdouble total_height_fraction = 0;\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tif (child->height_fraction <= 0) {\n\t\t\tif (current_height_fraction <= 0) {\n\t\t\t\tchild->height_fraction = 1.0;\n\t\t\t} else if (children->length > new_children) {\n\t\t\t\tchild->height_fraction = current_height_fraction /\n\t\t\t\t\t(children->length - new_children);\n\t\t\t} else {\n\t\t\t\tchild->height_fraction = current_height_fraction;\n\t\t\t}\n\t\t}\n\t\ttotal_height_fraction += child->height_fraction;\n\t}\n\t// Normalize height fractions so the sum is 1.0\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tchild->height_fraction /= total_height_fraction;\n\t}\n\n\t// Calculate gap size\n\tdouble inner_gap = 0;\n\tstruct sway_container *child = children->items[0];\n\tstruct sway_workspace *ws = child->pending.workspace;\n\tif (ws) {\n\t\tinner_gap = ws->gaps_inner;\n\t}\n\t// Descendants of tabbed/stacked containers don't have gaps\n\tstruct sway_container *temp = child;\n\twhile (temp) {\n\t\tenum sway_container_layout layout = container_parent_layout(temp);\n\t\tif (layout == L_TABBED || layout == L_STACKED) {\n\t\t\tinner_gap = 0;\n\t\t}\n\t\ttemp = temp->pending.parent;\n\t}\n\tdouble total_gap = fmin(inner_gap * (children->length - 1),\n\t\tfmax(0, parent->height - MIN_SANE_H * children->length));\n\tdouble child_total_height = parent->height - total_gap;\n\tinner_gap = floor(total_gap / (children->length - 1));\n\n\t// Resize windows\n\tsway_log(SWAY_DEBUG, \"Arranging %p vertically\", parent);\n\tdouble child_y = parent->y;\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tchild->child_total_height = child_total_height;\n\t\tchild->pending.x = parent->x;\n\t\tchild->pending.y = child_y;\n\t\tchild->pending.width = parent->width;\n\t\tchild->pending.height = round(child->height_fraction * child_total_height);\n\n\t\t// Make last child use remaining height of parent\n\t\tif (i == children->length - 1) {\n\t\t\tchild->pending.height = parent->y + parent->height - child->pending.y;\n\t\t}\n\n\t\t// Arbitrary lower bound for window size\n\t\tif (child->pending.width < 10 || child->pending.height < 10) {\n\t\t\tchild->pending.width = 0;\n\t\t\tchild->pending.height = 0;\n\t\t}\n\t\tchild_y += child->pending.height + inner_gap;\n\t}\n}\n\nstatic void apply_tabbed_layout(list_t *children, struct wlr_box *parent) {\n\tif (!children->length) {\n\t\treturn;\n\t}\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tint parent_offset = child->view ? 0 : container_titlebar_height();\n\t\tchild->pending.x = parent->x;\n\t\tchild->pending.y = parent->y + parent_offset;\n\t\tchild->pending.width = parent->width;\n\t\tchild->pending.height = parent->height - parent_offset;\n\t}\n}\n\nstatic void apply_stacked_layout(list_t *children, struct wlr_box *parent) {\n\tif (!children->length) {\n\t\treturn;\n\t}\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tint parent_offset = child->view ?  0 :\n\t\t\tcontainer_titlebar_height() * children->length;\n\t\tchild->pending.x = parent->x;\n\t\tchild->pending.y = parent->y + parent_offset;\n\t\tchild->pending.width = parent->width;\n\t\tchild->pending.height = parent->height - parent_offset;\n\t}\n}\n\nstatic void arrange_floating(list_t *floating) {\n\tfor (int i = 0; i < floating->length; ++i) {\n\t\tstruct sway_container *floater = floating->items[i];\n\t\tarrange_container(floater);\n\t}\n}\n\nstatic void arrange_children(list_t *children,\n\t\tenum sway_container_layout layout, struct wlr_box *parent) {\n\t// Calculate x, y, width and height of children\n\tswitch (layout) {\n\tcase L_HORIZ:\n\t\tapply_horiz_layout(children, parent);\n\t\tbreak;\n\tcase L_VERT:\n\t\tapply_vert_layout(children, parent);\n\t\tbreak;\n\tcase L_TABBED:\n\t\tapply_tabbed_layout(children, parent);\n\t\tbreak;\n\tcase L_STACKED:\n\t\tapply_stacked_layout(children, parent);\n\t\tbreak;\n\tcase L_NONE:\n\t\tapply_horiz_layout(children, parent);\n\t\tbreak;\n\t}\n\n\t// Recurse into child containers\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tstruct sway_container *child = children->items[i];\n\t\tarrange_container(child);\n\t}\n}\n\nvoid arrange_container(struct sway_container *container) {\n\tif (config->reloading) {\n\t\treturn;\n\t}\n\tif (container->view) {\n\t\tview_autoconfigure(container->view);\n\t\tnode_set_dirty(&container->node);\n\t\treturn;\n\t}\n\tstruct wlr_box box;\n\tcontainer_get_box(container, &box);\n\tarrange_children(container->pending.children, container->pending.layout, &box);\n\tnode_set_dirty(&container->node);\n}\n\nvoid arrange_workspace(struct sway_workspace *workspace) {\n\tif (config->reloading) {\n\t\treturn;\n\t}\n\tif (!workspace->output) {\n\t\t// Happens when there are no outputs connected\n\t\treturn;\n\t}\n\tstruct sway_output *output = workspace->output;\n\tstruct wlr_box *area = &output->usable_area;\n\tsway_log(SWAY_DEBUG, \"Usable area for ws: %dx%d@%d,%d\",\n\t\t\tarea->width, area->height, area->x, area->y);\n\n\tbool first_arrange = workspace->width == 0 && workspace->height == 0;\n\tstruct wlr_box prev_box;\n\tworkspace_get_box(workspace, &prev_box);\n\n\tdouble prev_x = workspace->x - workspace->current_gaps.left;\n\tdouble prev_y = workspace->y - workspace->current_gaps.top;\n\tworkspace->width = area->width;\n\tworkspace->height = area->height;\n\tworkspace->x = output->lx + area->x;\n\tworkspace->y = output->ly + area->y;\n\n\t// Adjust any floating containers\n\tdouble diff_x = workspace->x - prev_x;\n\tdouble diff_y = workspace->y - prev_y;\n\tif (!first_arrange && (diff_x != 0 || diff_y != 0)) {\n\t\tfor (int i = 0; i < workspace->floating->length; ++i) {\n\t\t\tstruct sway_container *floater = workspace->floating->items[i];\n\t\t\tstruct wlr_box workspace_box;\n\t\t\tworkspace_get_box(workspace, &workspace_box);\n\t\t\tfloating_fix_coordinates(floater, &prev_box, &workspace_box);\n\t\t\t// Set transformation for scratchpad windows.\n\t\t\tif (floater->scratchpad) {\n\t\t\t\tstruct wlr_box output_box;\n\t\t\t\toutput_get_box(output, &output_box);\n\t\t\t\tfloater->transform = output_box;\n\t\t\t}\n\t\t}\n\t}\n\n\tworkspace_add_gaps(workspace);\n\tnode_set_dirty(&workspace->node);\n\tsway_log(SWAY_DEBUG, \"Arranging workspace '%s' at %f, %f\", workspace->name,\n\t\t\tworkspace->x, workspace->y);\n\tif (workspace->fullscreen) {\n\t\tstruct sway_container *fs = workspace->fullscreen;\n\t\tfs->pending.x = output->lx;\n\t\tfs->pending.y = output->ly;\n\t\tfs->pending.width = output->width;\n\t\tfs->pending.height = output->height;\n\t\tarrange_container(fs);\n\t} else {\n\t\tstruct wlr_box box;\n\t\tworkspace_get_box(workspace, &box);\n\t\tarrange_children(workspace->tiling, workspace->layout, &box);\n\t\tarrange_floating(workspace->floating);\n\t}\n}\n\nvoid arrange_output(struct sway_output *output) {\n\tif (config->reloading) {\n\t\treturn;\n\t}\n\tif (!output->wlr_output->enabled) {\n\t\treturn;\n\t}\n\tfor (int i = 0; i < output->workspaces->length; ++i) {\n\t\tstruct sway_workspace *workspace = output->workspaces->items[i];\n\t\tarrange_workspace(workspace);\n\t}\n}\n\nvoid arrange_root(void) {\n\tif (config->reloading) {\n\t\treturn;\n\t}\n\tstruct wlr_box layout_box;\n\twlr_output_layout_get_box(root->output_layout, NULL, &layout_box);\n\troot->x = layout_box.x;\n\troot->y = layout_box.y;\n\troot->width = layout_box.width;\n\troot->height = layout_box.height;\n\n\tif (root->fullscreen_global) {\n\t\tstruct sway_container *fs = root->fullscreen_global;\n\t\tfs->pending.x = root->x;\n\t\tfs->pending.y = root->y;\n\t\tfs->pending.width = root->width;\n\t\tfs->pending.height = root->height;\n\t\tarrange_container(fs);\n\t} else {\n\t\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tarrange_output(output);\n\t\t}\n\t}\n}\n\nvoid arrange_node(struct sway_node *node) {\n\tswitch (node->type) {\n\tcase N_ROOT:\n\t\tarrange_root();\n\t\tbreak;\n\tcase N_OUTPUT:\n\t\tarrange_output(node->sway_output);\n\t\tbreak;\n\tcase N_WORKSPACE:\n\t\tarrange_workspace(node->sway_workspace);\n\t\tbreak;\n\tcase N_CONTAINER:\n\t\tarrange_container(node->sway_container);\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "sway/tree/container.c",
    "content": "#include <assert.h>\n#include <drm_fourcc.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <wayland-server-core.h>\n#include <wlr/types/wlr_foreign_toplevel_management_v1.h>\n#include <wlr/types/wlr_linux_dmabuf_v1.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_subcompositor.h>\n#include \"sway/config.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/sway_text_node.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/xdg_decoration.h\"\n#include \"list.h\"\n#include \"pango.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic void handle_outputs_update(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_container *con = wl_container_of(\n\t\t\tlistener, con, outputs_update);\n\tstruct wlr_scene_outputs_update_event *event = data;\n\n\tstruct wlr_foreign_toplevel_handle_v1 *toplevel = con->view->foreign_toplevel;\n\tif (toplevel) {\n\t\tstruct wlr_foreign_toplevel_handle_v1_output *toplevel_output, *tmp;\n\t\twl_list_for_each_safe(toplevel_output, tmp, &toplevel->outputs, link) {\n\t\t\tbool active = false;\n\t\t\tfor (size_t i = 0; i < event->size; i++) {\n\t\t\t\tstruct wlr_scene_output *scene_output = event->active[i];\n\t\t\t\tif (scene_output->output == toplevel_output->output) {\n\t\t\t\t\tactive = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!active) {\n\t\t\t\twlr_foreign_toplevel_handle_v1_output_leave(toplevel, toplevel_output->output);\n\t\t\t}\n\t\t}\n\n\t\tfor (size_t i = 0; i < event->size; i++) {\n\t\t\tstruct wlr_scene_output *scene_output = event->active[i];\n\t\t\twlr_foreign_toplevel_handle_v1_output_enter(toplevel, scene_output->output);\n\t\t}\n\t}\n}\n\nstatic void handle_destroy(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_container *con = wl_container_of(\n\t\t\tlistener, con, output_handler_destroy);\n\n\tcontainer_begin_destroy(con);\n}\n\nstatic bool handle_point_accepts_input(\n\t\tstruct wlr_scene_buffer *buffer, double *x, double *y) {\n\treturn false;\n}\n\nstatic struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent,\n\t\tbool *failed) {\n\tif (*failed) {\n\t\treturn NULL;\n\t}\n\n\t// just pass in random values. These will be overwritten when\n\t// they need to be used.\n\tstruct wlr_scene_rect *rect = wlr_scene_rect_create(\n\t\tparent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});\n\tif (!rect) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate a wlr_scene_rect\");\n\t\t*failed = true;\n\t}\n\n\treturn rect;\n}\n\nstruct sway_container *container_create(struct sway_view *view) {\n\tstruct sway_container *c = calloc(1, sizeof(struct sway_container));\n\tif (!c) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate sway_container\");\n\t\treturn NULL;\n\t}\n\tnode_init(&c->node, N_CONTAINER, c);\n\n\t// Container tree structure\n\t// - scene tree\n\t//   - title bar\n\t//     - border\n\t//     - background\n\t//     - title text\n\t//     - marks text\n\t//   - border\n\t//     - border top/bottom/left/right\n\t//     - content_tree (we put the content node here so when we disable the\n\t//       border everything gets disabled. We only render the content iff there\n\t//       is a border as well)\n\t//     - buffer used for output enter/leave events for foreign_toplevel\n\tbool failed = false;\n\tc->scene_tree = alloc_scene_tree(root->staging, &failed);\n\n\tc->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed);\n\tc->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed);\n\tc->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed);\n\n\t// for opacity purposes we need to carfully create the scene such that\n\t// none of our rect nodes as well as text buffers don't overlap. To do\n\t// this we have to create rects such that they go around text buffers\n\tfor (int i = 0; i < 4; i++) {\n\t\talloc_rect_node(c->title_bar.border, &failed);\n\t}\n\n\tfor (int i = 0; i < 5; i++) {\n\t\talloc_rect_node(c->title_bar.background, &failed);\n\t}\n\n\tc->border.tree = alloc_scene_tree(c->scene_tree, &failed);\n\tc->content_tree = alloc_scene_tree(c->border.tree, &failed);\n\n\tif (view) {\n\t\t// only containers with views can have borders\n\t\tc->border.top = alloc_rect_node(c->border.tree, &failed);\n\t\tc->border.bottom = alloc_rect_node(c->border.tree, &failed);\n\t\tc->border.left = alloc_rect_node(c->border.tree, &failed);\n\t\tc->border.right = alloc_rect_node(c->border.tree, &failed);\n\n\t\tc->output_handler = wlr_scene_buffer_create(c->border.tree, NULL);\n\t\tif (!c->output_handler) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to allocate a scene node\");\n\t\t\tfailed = true;\n\t\t}\n\n\t\tif (!failed) {\n\t\t\tc->outputs_update.notify = handle_outputs_update;\n\t\t\twl_signal_add(&c->output_handler->events.outputs_update,\n\t\t\t\t\t&c->outputs_update);\n\t\t\tc->output_handler_destroy.notify = handle_destroy;\n\t\t\twl_signal_add(&c->output_handler->node.events.destroy,\n\t\t\t\t\t&c->output_handler_destroy);\n\t\t\tc->output_handler->point_accepts_input = handle_point_accepts_input;\n\t\t}\n\t}\n\n\tif (!failed && !scene_descriptor_assign(&c->scene_tree->node,\n\t\t\tSWAY_SCENE_DESC_CONTAINER, c)) {\n\t\tfailed = true;\n\t}\n\n\tif (failed) {\n\t\twlr_scene_node_destroy(&c->scene_tree->node);\n\t\tfree(c);\n\t\treturn NULL;\n\t}\n\n\tif (!view) {\n\t\tc->pending.children = create_list();\n\t\tc->current.children = create_list();\n\t}\n\n\tc->pending.layout = L_NONE;\n\tc->view = view;\n\tc->alpha = 1.0f;\n\tc->marks = create_list();\n\n\twl_signal_init(&c->events.destroy);\n\twl_signal_emit_mutable(&root->events.new_node, &c->node);\n\n\tcontainer_update(c);\n\n\treturn c;\n}\n\nstatic bool container_is_focused(struct sway_container *con, void *data) {\n\treturn con->current.focused;\n}\n\nstatic bool container_has_focused_child(struct sway_container *con) {\n\treturn container_find_child(con, container_is_focused, NULL);\n}\n\nstatic bool container_is_current_parent_focused(struct sway_container *con) {\n\tif (con->current.parent) {\n\t\tstruct sway_container *parent = con->current.parent;\n\t\treturn parent->current.focused || container_is_current_parent_focused(parent);\n\t} else if (con->current.workspace) {\n\t\tstruct sway_workspace *ws = con->current.workspace;\n\t\treturn ws->current.focused;\n\t}\n\n\treturn false;\n}\n\nstatic struct border_colors *container_get_current_colors(\n\t\tstruct sway_container *con) {\n\tstruct border_colors *colors;\n\n\tbool urgent = con->view ?\n\t\tview_is_urgent(con->view) : container_has_urgent_child(con);\n\tstruct sway_container *active_child;\n\n\tif (con->current.parent) {\n\t\tactive_child = con->current.parent->current.focused_inactive_child;\n\t} else if (con->current.workspace) {\n\t\tactive_child = con->current.workspace->current.focused_inactive_child;\n\t} else {\n\t\tactive_child = NULL;\n\t}\n\n\tif (urgent) {\n\t\tcolors = &config->border_colors.urgent;\n\t} else if (con->current.focused || container_is_current_parent_focused(con)) {\n\t\tcolors = &config->border_colors.focused;\n\t} else if (config->has_focused_tab_title && container_has_focused_child(con)) {\n\t\tcolors = &config->border_colors.focused_tab_title;\n\t} else if (con == active_child) {\n\t\tcolors = &config->border_colors.focused_inactive;\n\t} else {\n\t\tcolors = &config->border_colors.unfocused;\n\t}\n\n\treturn colors;\n}\n\nstatic bool container_is_current_floating(struct sway_container *container) {\n\tif (!container->current.parent && container->current.workspace &&\n\t\t\tlist_find(container->current.workspace->floating, container) != -1) {\n\t\treturn true;\n\t}\n\tif (container->scratchpad) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n// scene rect wants premultiplied colors\nstatic void scene_rect_set_color(struct wlr_scene_rect *rect,\n\t\tconst float color[4], float opacity) {\n\tconst float premultiplied[] = {\n\t\tcolor[0] * color[3] * opacity,\n\t\tcolor[1] * color[3] * opacity,\n\t\tcolor[2] * color[3] * opacity,\n\t\tcolor[3] * opacity,\n\t};\n\n\twlr_scene_rect_set_color(rect, premultiplied);\n}\n\nvoid container_update(struct sway_container *con) {\n\tstruct border_colors *colors = container_get_current_colors(con);\n\tlist_t *siblings = NULL;\n\tenum sway_container_layout layout = L_NONE;\n\tfloat alpha = con->alpha;\n\n\tif (con->current.parent) {\n\t\tsiblings = con->current.parent->current.children;\n\t\tlayout = con->current.parent->current.layout;\n\t} else if (con->current.workspace) {\n\t\tsiblings = con->current.workspace->current.tiling;\n\t\tlayout = con->current.workspace->current.layout;\n\t}\n\n\tfloat bottom[4], right[4];\n\tmemcpy(bottom, colors->child_border, sizeof(bottom));\n\tmemcpy(right, colors->child_border, sizeof(right));\n\n\tif (!container_is_current_floating(con) && siblings && siblings->length == 1) {\n\t\tif (layout == L_HORIZ) {\n\t\t\tmemcpy(right, colors->indicator, sizeof(right));\n\t\t} else if (layout == L_VERT) {\n\t\t\tmemcpy(bottom, colors->indicator, sizeof(bottom));\n\t\t}\n\t}\n\n\tstruct wlr_scene_node *node;\n\twl_list_for_each(node, &con->title_bar.border->children, link) {\n\t\tstruct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);\n\t\tscene_rect_set_color(rect, colors->border, alpha);\n\t}\n\n\twl_list_for_each(node, &con->title_bar.background->children, link) {\n\t\tstruct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);\n\t\tscene_rect_set_color(rect, colors->background, alpha);\n\t}\n\n\tif (con->view) {\n\t\tscene_rect_set_color(con->border.top, colors->child_border, alpha);\n\t\tscene_rect_set_color(con->border.bottom, bottom, alpha);\n\t\tscene_rect_set_color(con->border.left, colors->child_border, alpha);\n\t\tscene_rect_set_color(con->border.right, right, alpha);\n\t}\n\n\tif (con->title_bar.title_text) {\n\t\tsway_text_node_set_color(con->title_bar.title_text, colors->text);\n\t\tsway_text_node_set_background(con->title_bar.title_text, colors->background);\n\t}\n\n\tif (con->title_bar.marks_text) {\n\t\tsway_text_node_set_color(con->title_bar.marks_text, colors->text);\n\t\tsway_text_node_set_background(con->title_bar.marks_text, colors->background);\n\t}\n}\n\nvoid container_update_itself_and_parents(struct sway_container *con) {\n\tcontainer_update(con);\n\n\tif (con->current.parent) {\n\t\tcontainer_update_itself_and_parents(con->current.parent);\n\t}\n}\n\nstatic void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) {\n\tint len;\n\tconst pixman_box32_t *rects = pixman_region32_rectangles(region, &len);\n\n\twlr_scene_node_set_enabled(&tree->node, len > 0);\n\tif (len == 0) {\n\t\treturn;\n\t}\n\n\tint i = 0;\n\tstruct wlr_scene_node *node;\n\twl_list_for_each(node, &tree->children, link) {\n\t\tstruct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);\n\t\twlr_scene_node_set_enabled(&rect->node, i < len);\n\n\t\tif (i < len) {\n\t\t\tconst pixman_box32_t *box = &rects[i++];\n\t\t\twlr_scene_node_set_position(&rect->node, box->x1, box->y1);\n\t\t\twlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1);\n\t\t}\n\t}\n}\n\nvoid container_arrange_title_bar(struct sway_container *con) {\n\tenum alignment title_align = config->title_align;\n\tint marks_buffer_width = 0;\n\tint width = con->title_width;\n\tint height = container_titlebar_height();\n\n\tpixman_region32_t text_area;\n\tpixman_region32_init(&text_area);\n\n\tif (con->title_bar.marks_text) {\n\t\tstruct sway_text_node *node = con->title_bar.marks_text;\n\t\tmarks_buffer_width = node->width;\n\n\t\tint h_padding;\n\t\tif (title_align == ALIGN_RIGHT) {\n\t\t\th_padding = config->titlebar_h_padding;\n\t\t} else {\n\t\t\th_padding = width - config->titlebar_h_padding - marks_buffer_width;\n\t\t}\n\n\t\th_padding = MAX(h_padding, config->titlebar_h_padding);\n\n\t\tint alloc_width = MIN((int)node->width,\n\t\t\twidth - h_padding - config->titlebar_h_padding);\n\t\talloc_width = MAX(alloc_width, 0);\n\n\t\tsway_text_node_set_max_width(node, alloc_width);\n\t\twlr_scene_node_set_position(node->node,\n\t\t\th_padding, (height - node->height) >> 1);\n\n\t\tpixman_region32_union_rect(&text_area, &text_area,\n\t\t\tnode->node->x, node->node->y, alloc_width, node->height);\n\t}\n\n\tif (con->title_bar.title_text) {\n\t\tstruct sway_text_node *node = con->title_bar.title_text;\n\n\t\tint h_padding;\n\t\tif (title_align == ALIGN_RIGHT) {\n\t\t\th_padding = width - config->titlebar_h_padding - node->width;\n\t\t} else if (title_align == ALIGN_CENTER) {\n\t\t\th_padding = ((int)width - marks_buffer_width - node->width) >> 1;\n\t\t} else {\n\t\t\th_padding = config->titlebar_h_padding;\n\t\t}\n\n\t\th_padding = MAX(h_padding, config->titlebar_h_padding);\n\n\t\tint alloc_width = MIN((int) node->width,\n\t\t\twidth - h_padding - config->titlebar_h_padding);\n\t\talloc_width = MAX(alloc_width, 0);\n\n\t\tsway_text_node_set_max_width(node, alloc_width);\n\t\twlr_scene_node_set_position(node->node,\n\t\t\th_padding, (height - node->height) >> 1);\n\n\t\tpixman_region32_union_rect(&text_area, &text_area,\n\t\t\tnode->node->x, node->node->y, alloc_width, node->height);\n\t}\n\n\t// silence pixman errors\n\tif (width <= 0 || height <= 0) {\n\t\tpixman_region32_fini(&text_area);\n\t\treturn;\n\t}\n\n\tpixman_region32_t background, border;\n\n\tint thickness = config->titlebar_border_thickness;\n\tpixman_region32_init_rect(&background,\n\t\tthickness, thickness,\n\t\twidth - thickness * 2, height - thickness * 2);\n\tpixman_region32_init_rect(&border, 0, 0, width, height);\n\tpixman_region32_subtract(&border, &border, &background);\n\n\tpixman_region32_subtract(&background, &background, &text_area);\n\tpixman_region32_fini(&text_area);\n\n\tupdate_rect_list(con->title_bar.background, &background);\n\tpixman_region32_fini(&background);\n\n\tupdate_rect_list(con->title_bar.border, &border);\n\tpixman_region32_fini(&border);\n\n\tcontainer_update(con);\n}\n\nvoid container_update_marks(struct sway_container *con) {\n\tchar *buffer = NULL;\n\n\tif (config->show_marks && con->marks->length) {\n\t\tsize_t len = 0;\n\t\tfor (int i = 0; i < con->marks->length; ++i) {\n\t\t\tchar *mark = con->marks->items[i];\n\t\t\tif (mark[0] != '_') {\n\t\t\t\tlen += strlen(mark) + 2;\n\t\t\t}\n\t\t}\n\t\tbuffer = calloc(len + 1, 1);\n\t\tchar *part = malloc(len + 1);\n\n\t\tif (!sway_assert(buffer && part, \"Unable to allocate memory\")) {\n\t\t\tfree(buffer);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (int i = 0; i < con->marks->length; ++i) {\n\t\t\tchar *mark = con->marks->items[i];\n\t\t\tif (mark[0] != '_') {\n\t\t\t\tsnprintf(part, len + 1, \"[%s]\", mark);\n\t\t\t\tstrcat(buffer, part);\n\t\t\t}\n\t\t}\n\t\tfree(part);\n\t}\n\n\tif (!buffer) {\n\t\tif (con->title_bar.marks_text) {\n\t\t\twlr_scene_node_destroy(con->title_bar.marks_text->node);\n\t\t\tcon->title_bar.marks_text = NULL;\n\t\t}\n\t} else if (!con->title_bar.marks_text) {\n\t\tstruct border_colors *colors = container_get_current_colors(con);\n\n\t\tcon->title_bar.marks_text = sway_text_node_create(con->title_bar.tree,\n\t\t\tbuffer, colors->text, false);\n\t} else {\n\t\tsway_text_node_set_text(con->title_bar.marks_text, buffer);\n\t}\n\n\tcontainer_arrange_title_bar(con);\n\tfree(buffer);\n}\n\nvoid container_update_title_bar(struct sway_container *con) {\n\tif (!con->formatted_title) {\n\t\treturn;\n\t}\n\n\tstruct border_colors *colors = container_get_current_colors(con);\n\n\tif (con->title_bar.title_text) {\n\t\twlr_scene_node_destroy(con->title_bar.title_text->node);\n\t\tcon->title_bar.title_text = NULL;\n\t}\n\n\tcon->title_bar.title_text = sway_text_node_create(con->title_bar.tree,\n\t\tcon->formatted_title, colors->text, config->pango_markup);\n\n\t// we always have to remake these text buffers completely for text font\n\t// changes etc...\n\tif (con->title_bar.marks_text) {\n\t\twlr_scene_node_destroy(con->title_bar.marks_text->node);\n\t\tcon->title_bar.marks_text = NULL;\n\t}\n\n\tcontainer_update_marks(con);\n\tcontainer_arrange_title_bar(con);\n}\n\nvoid container_destroy(struct sway_container *con) {\n\tif (!sway_assert(con->node.destroying,\n\t\t\t\t\"Tried to free container which wasn't marked as destroying\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(con->node.ntxnrefs == 0, \"Tried to free container \"\n\t\t\t\t\"which is still referenced by transactions\")) {\n\t\treturn;\n\t}\n\tfree(con->title);\n\tfree(con->formatted_title);\n\tfree(con->title_format);\n\tlist_free(con->pending.children);\n\tlist_free(con->current.children);\n\n\tlist_free_items_and_destroy(con->marks);\n\n\tif (con->view && con->view->container == con) {\n\t\tcon->view->container = NULL;\n\t\twlr_scene_node_destroy(&con->output_handler->node);\n\t\tif (con->view->destroying) {\n\t\t\tview_destroy(con->view);\n\t\t}\n\t}\n\n\tscene_node_disown_children(con->content_tree);\n\twlr_scene_node_destroy(&con->scene_tree->node);\n\tfree(con);\n}\n\nvoid container_begin_destroy(struct sway_container *con) {\n\tif (con->view) {\n\t\tipc_event_window(con, \"close\");\n\t}\n\t// The workspace must have the fullscreen pointer cleared so that the\n\t// seat code can find an appropriate new focus.\n\tif (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) {\n\t\tcon->pending.workspace->fullscreen = NULL;\n\t}\n\tif (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\tcontainer_fullscreen_disable(con);\n\t}\n\n\twl_signal_emit_mutable(&con->node.events.destroy, &con->node);\n\n\tcontainer_end_mouse_operation(con);\n\n\tnode_set_dirty(&con->node);\n\tcon->node.destroying = true;\n\n\tif (con->scratchpad) {\n\t\troot_scratchpad_remove_container(con);\n\t}\n\n\tif (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\tcontainer_fullscreen_disable(con);\n\t}\n\n\tif (con->pending.parent || con->pending.workspace) {\n\t\tcontainer_detach(con);\n\t}\n\n\tif (con->view && con->view->container == con) {\n\t\twl_list_remove(&con->outputs_update.link);\n\t\twl_list_remove(&con->output_handler_destroy.link);\n\t}\n}\n\nvoid container_reap_empty(struct sway_container *con) {\n\tif (con->view) {\n\t\treturn;\n\t}\n\tstruct sway_workspace *ws = con->pending.workspace;\n\twhile (con) {\n\t\tif (con->pending.children->length) {\n\t\t\treturn;\n\t\t}\n\t\tstruct sway_container *parent = con->pending.parent;\n\t\tcontainer_begin_destroy(con);\n\t\tcon = parent;\n\t}\n\tif (ws) {\n\t\tworkspace_consider_destroy(ws);\n\t}\n}\n\nstruct sway_container *container_flatten(struct sway_container *container) {\n\tif (container->view) {\n\t\treturn NULL;\n\t}\n\twhile (container && container->pending.children->length == 1) {\n\t\tstruct sway_container *child = container->pending.children->items[0];\n\t\tstruct sway_container *parent = container->pending.parent;\n\t\tcontainer_replace(container, child);\n\t\tcontainer_begin_destroy(container);\n\t\tcontainer = parent;\n\t}\n\treturn container;\n}\n\nstruct sway_container *container_find_child(struct sway_container *container,\n\t\tbool (*test)(struct sway_container *con, void *data), void *data) {\n\tif (!container->pending.children) {\n\t\treturn NULL;\n\t}\n\tfor (int i = 0; i < container->pending.children->length; ++i) {\n\t\tstruct sway_container *child = container->pending.children->items[i];\n\t\tif (test(child, data)) {\n\t\t\treturn child;\n\t\t}\n\t\tstruct sway_container *res = container_find_child(child, test, data);\n\t\tif (res) {\n\t\t\treturn res;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nvoid container_for_each_child(struct sway_container *container,\n\t\tvoid (*f)(struct sway_container *container, void *data),\n\t\tvoid *data) {\n\tif (container->pending.children)  {\n\t\tfor (int i = 0; i < container->pending.children->length; ++i) {\n\t\t\tstruct sway_container *child = container->pending.children->items[i];\n\t\t\tf(child, data);\n\t\t\tcontainer_for_each_child(child, f, data);\n\t\t}\n\t}\n}\n\nstruct sway_container *container_obstructing_fullscreen_container(struct sway_container *container)\n{\n\tstruct sway_workspace *workspace = container->pending.workspace;\n\n\tif (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) {\n\t\tif (container_is_transient_for(container, workspace->fullscreen)) {\n\t\t\treturn NULL;\n\t\t}\n\t\treturn workspace->fullscreen;\n\t}\n\n\tstruct sway_container *fullscreen_global = root->fullscreen_global;\n\tif (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) {\n\t\tif (container_is_transient_for(container, fullscreen_global)) {\n\t\t\treturn NULL;\n\t\t}\n\t\treturn fullscreen_global;\n\t}\n\n\treturn NULL;\n}\n\nbool container_has_ancestor(struct sway_container *descendant,\n\t\tstruct sway_container *ancestor) {\n\twhile (descendant) {\n\t\tdescendant = descendant->pending.parent;\n\t\tif (descendant == ancestor) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic char *escape_pango_markup(const char *buffer) {\n\tsize_t length = escape_markup_text(buffer, NULL);\n\tchar *escaped_title = calloc(length + 1, sizeof(char));\n\tescape_markup_text(buffer, escaped_title);\n\treturn escaped_title;\n}\n\nstatic size_t append_prop(char *buffer, const char *value) {\n\tif (!value) {\n\t\treturn 0;\n\t}\n\t// If using pango_markup in font, we need to escape all markup chars\n\t// from values to make sure tags are not inserted by clients\n\tif (config->pango_markup) {\n\t\tchar *escaped_value = escape_pango_markup(value);\n\t\tlenient_strcat(buffer, escaped_value);\n\t\tsize_t len = strlen(escaped_value);\n\t\tfree(escaped_value);\n\t\treturn len;\n\t} else {\n\t\tlenient_strcat(buffer, value);\n\t\treturn strlen(value);\n\t}\n}\n\n/**\n * Calculate and return the length of the formatted title.\n * If buffer is not NULL, also populate the buffer with the formatted title.\n */\nsize_t parse_title_format(struct sway_container *container, char *buffer) {\n\tif (!container->title_format || strcmp(container->title_format, \"%title\") == 0) {\n\t\tif (container->view) {\n\t\t\treturn append_prop(buffer, view_get_title(container->view));\n\t\t} else {\n\t\t\treturn container_build_representation(container->pending.layout, container->pending.children, buffer);\n\t\t}\n\t}\n\n\tsize_t len = 0;\n\tchar *format = container->title_format;\n\tchar *next = strchr(format, '%');\n\twhile (next) {\n\t\t// Copy everything up to the %\n\t\tlenient_strncat(buffer, format, next - format);\n\t\tlen += next - format;\n\t\tformat = next;\n\n\t\tif (has_prefix(next, \"%title\")) {\n\t\t\tif (container->view) {\n\t\t\t\tlen += append_prop(buffer, view_get_title(container->view));\n\t\t\t} else {\n\t\t\t\tlen += container_build_representation(container->pending.layout, container->pending.children, buffer);\n\t\t\t}\n\t\t\tformat += strlen(\"%title\");\n\t\t} else if (container->view) {\n\t\t\tif (has_prefix(next, \"%app_id\")) {\n\t\t\t\tlen += append_prop(buffer, view_get_app_id(container->view));\n\t\t\t\tformat += strlen(\"%app_id\");\n\t\t\t} else if (has_prefix(next, \"%class\")) {\n\t\t\t\tlen += append_prop(buffer, view_get_class(container->view));\n\t\t\t\tformat += strlen(\"%class\");\n\t\t\t} else if (has_prefix(next, \"%instance\")) {\n\t\t\t\tlen += append_prop(buffer, view_get_instance(container->view));\n\t\t\t\tformat += strlen(\"%instance\");\n\t\t\t} else if (has_prefix(next, \"%shell\")) {\n\t\t\t\tlen += append_prop(buffer, view_get_shell(container->view));\n\t\t\t\tformat += strlen(\"%shell\");\n\t\t\t} else if (has_prefix(next, \"%sandbox_engine\")) {\n\t\t\t\tlen += append_prop(buffer, view_get_sandbox_engine(container->view));\n\t\t\t\tformat += strlen(\"%sandbox_engine\");\n\t\t\t} else if (has_prefix(next, \"%sandbox_app_id\")) {\n\t\t\t\tlen += append_prop(buffer, view_get_sandbox_app_id(container->view));\n\t\t\t\tformat += strlen(\"%sandbox_app_id\");\n\t\t\t} else if (has_prefix(next, \"%sandbox_instance_id\")) {\n\t\t\t\tlen += append_prop(buffer, view_get_sandbox_instance_id(container->view));\n\t\t\t\tformat += strlen(\"%sandbox_instance_id\");\n\t\t\t} else {\n\t\t\t\tlenient_strcat(buffer, \"%\");\n\t\t\t\t++format;\n\t\t\t\t++len;\n\t\t\t}\n\t\t} else {\n\t\t\tlenient_strcat(buffer, \"%\");\n\t\t\t++format;\n\t\t\t++len;\n\t\t}\n\t\tnext = strchr(format, '%');\n\t}\n\tlenient_strcat(buffer, format);\n\tlen += strlen(format);\n\n\treturn len;\n}\n\n/**\n * Calculate and return the length of the tree representation.\n * An example tree representation is: V[Terminal, Firefox]\n * If buffer is not NULL, also populate the buffer with the representation.\n */\nsize_t container_build_representation(enum sway_container_layout layout,\n\t\tlist_t *children, char *buffer) {\n\tsize_t len = 2;\n\tswitch (layout) {\n\tcase L_VERT:\n\t\tlenient_strcat(buffer, \"V[\");\n\t\tbreak;\n\tcase L_HORIZ:\n\t\tlenient_strcat(buffer, \"H[\");\n\t\tbreak;\n\tcase L_TABBED:\n\t\tlenient_strcat(buffer, \"T[\");\n\t\tbreak;\n\tcase L_STACKED:\n\t\tlenient_strcat(buffer, \"S[\");\n\t\tbreak;\n\tcase L_NONE:\n\t\tlenient_strcat(buffer, \"D[\");\n\t\tbreak;\n\t}\n\tfor (int i = 0; i < children->length; ++i) {\n\t\tif (i != 0) {\n\t\t\t++len;\n\t\t\tlenient_strcat(buffer, \" \");\n\t\t}\n\t\tstruct sway_container *child = children->items[i];\n\t\tconst char *identifier = NULL;\n\t\tif (child->view) {\n\t\t\tidentifier = view_get_class(child->view);\n\t\t\tif (!identifier) {\n\t\t\t\tidentifier = view_get_app_id(child->view);\n\t\t\t}\n\t\t} else {\n\t\t\tidentifier = child->formatted_title;\n\t\t}\n\t\tif (identifier) {\n\t\t\tlen += strlen(identifier);\n\t\t\tlenient_strcat(buffer, identifier);\n\t\t} else {\n\t\t\tlen += strlen(\"(null)\");\n\t\t\tlenient_strcat(buffer, \"(null)\");\n\t\t}\n\t}\n\t++len;\n\tlenient_strcat(buffer, \"]\");\n\treturn len;\n}\n\nvoid container_update_representation(struct sway_container *con) {\n\tif (!con->view) {\n\t\tsize_t len = parse_title_format(con, NULL);\n\t\tfree(con->formatted_title);\n\t\tcon->formatted_title = calloc(len + 1, sizeof(char));\n\t\tif (!sway_assert(con->formatted_title,\n\t\t\t\t\t\"Unable to allocate title string\")) {\n\t\t\treturn;\n\t\t}\n\t\tparse_title_format(con, con->formatted_title);\n\n\t\tif (con->title_bar.title_text) {\n\t\t\tsway_text_node_set_text(con->title_bar.title_text, con->formatted_title);\n\t\t\tcontainer_arrange_title_bar(con);\n\t\t} else {\n\t\t\tcontainer_update_title_bar(con);\n\t\t}\n\t}\n\tif (con->pending.parent) {\n\t\tcontainer_update_representation(con->pending.parent);\n\t} else if (con->pending.workspace) {\n\t\tworkspace_update_representation(con->pending.workspace);\n\t}\n}\n\nsize_t container_titlebar_height(void) {\n\treturn config->font_height + config->titlebar_v_padding * 2;\n}\n\nvoid floating_calculate_constraints(int *min_width, int *max_width,\n\t\tint *min_height, int *max_height) {\n\tif (config->floating_minimum_width == -1) { // no minimum\n\t\t*min_width = 0;\n\t} else if (config->floating_minimum_width == 0) { // automatic\n\t\t*min_width = 75;\n\t} else {\n\t\t*min_width = config->floating_minimum_width;\n\t}\n\n\tif (config->floating_minimum_height == -1) { // no minimum\n\t\t*min_height = 0;\n\t} else if (config->floating_minimum_height == 0) { // automatic\n\t\t*min_height = 50;\n\t} else {\n\t\t*min_height = config->floating_minimum_height;\n\t}\n\n\tstruct wlr_box box;\n\twlr_output_layout_get_box(root->output_layout, NULL, &box);\n\n\tif (config->floating_maximum_width == -1) { // no maximum\n\t\t*max_width = INT_MAX;\n\t} else if (config->floating_maximum_width == 0) { // automatic\n\t\t*max_width = box.width;\n\t} else {\n\t\t*max_width = config->floating_maximum_width;\n\t}\n\n\tif (config->floating_maximum_height == -1) { // no maximum\n\t\t*max_height = INT_MAX;\n\t} else if (config->floating_maximum_height == 0) { // automatic\n\t\t*max_height = box.height;\n\t} else {\n\t\t*max_height = config->floating_maximum_height;\n\t}\n\n}\n\nvoid floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) {\n\tif (!old->width || !old->height) {\n\t\t// Fall back to centering on the workspace.\n\t\tcontainer_floating_move_to_center(con);\n\t} else {\n\t\tdouble rel_x = con->pending.x - old->x + (con->pending.width / 2);\n\t\tdouble rel_y = con->pending.y - old->y + (con->pending.height / 2);\n\n\t\tcon->pending.x = new->x + (rel_x * new->width) / old->width - (con->pending.width / 2);\n\t\tcon->pending.y = new->y + (rel_y * new->height) / old->height - (con->pending.height / 2);\n\n\t\tsway_log(SWAY_DEBUG, \"Transformed container %p to coords (%f, %f)\", con, con->pending.x, con->pending.y);\n\t}\n}\n\nstatic void floating_natural_resize(struct sway_container *con) {\n\tint min_width, max_width, min_height, max_height;\n\tfloating_calculate_constraints(&min_width, &max_width,\n\t\t\t&min_height, &max_height);\n\tif (!con->view) {\n\t\tcon->pending.width = fmax(min_width, fmin(con->pending.width, max_width));\n\t\tcon->pending.height = fmax(min_height, fmin(con->pending.height, max_height));\n\t} else {\n\t\tstruct sway_view *view = con->view;\n\t\tcon->pending.content_width =\n\t\t\tfmax(min_width, fmin(view->natural_width, max_width));\n\t\tcon->pending.content_height =\n\t\t\tfmax(min_height, fmin(view->natural_height, max_height));\n\t\tcontainer_set_geometry_from_content(con);\n\t}\n}\n\nvoid container_floating_resize_and_center(struct sway_container *con) {\n\tstruct sway_workspace *ws = con->pending.workspace;\n\tif (!ws) {\n\t\t// On scratchpad, just resize\n\t\tfloating_natural_resize(con);\n\t\treturn;\n\t}\n\n\tstruct wlr_box ob;\n\twlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob);\n\tif (wlr_box_empty(&ob)) {\n\t\t// On NOOP output. Will be called again when moved to an output\n\t\tcon->pending.x = 0;\n\t\tcon->pending.y = 0;\n\t\tcon->pending.width = 0;\n\t\tcon->pending.height = 0;\n\t\treturn;\n\t}\n\n\tfloating_natural_resize(con);\n\tif (!con->view) {\n\t\tif (con->pending.width > ws->width || con->pending.height > ws->height) {\n\t\t\tcon->pending.x = ob.x + (ob.width - con->pending.width) / 2;\n\t\t\tcon->pending.y = ob.y + (ob.height - con->pending.height) / 2;\n\t\t} else {\n\t\t\tcon->pending.x = ws->x + (ws->width - con->pending.width) / 2;\n\t\t\tcon->pending.y = ws->y + (ws->height - con->pending.height) / 2;\n\t\t}\n\t} else {\n\t\tif (con->pending.content_width > ws->width\n\t\t\t\t|| con->pending.content_height > ws->height) {\n\t\t\tcon->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2;\n\t\t\tcon->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2;\n\t\t} else {\n\t\t\tcon->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2;\n\t\t\tcon->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;\n\t\t}\n\n\t\t// If the view's border is B_NONE then these properties are ignored.\n\t\tcon->pending.border_top = con->pending.border_bottom = true;\n\t\tcon->pending.border_left = con->pending.border_right = true;\n\n\t\tcontainer_set_geometry_from_content(con);\n\t}\n}\n\nvoid container_floating_set_default_size(struct sway_container *con) {\n\tif (!sway_assert(con->pending.workspace, \"Expected a container on a workspace\")) {\n\t\treturn;\n\t}\n\n\tint min_width, max_width, min_height, max_height;\n\tfloating_calculate_constraints(&min_width, &max_width,\n\t\t\t&min_height, &max_height);\n\tstruct wlr_box box;\n\tworkspace_get_box(con->pending.workspace, &box);\n\n\tdouble width = fmax(min_width, fmin(box.width * 0.5, max_width));\n\tdouble height = fmax(min_height, fmin(box.height * 0.75, max_height));\n\tif (!con->view) {\n\t\tcon->pending.width = width;\n\t\tcon->pending.height = height;\n\t} else {\n\t\tcon->pending.content_width = width;\n\t\tcon->pending.content_height = height;\n\t\tcontainer_set_geometry_from_content(con);\n\t}\n}\n\n\n/**\n * Indicate to clients in this container that they are participating in (or\n * have just finished) an interactive resize\n */\nvoid container_set_resizing(struct sway_container *con, bool resizing) {\n\tif (!con) {\n\t\treturn;\n\t}\n\n\tif (con->view) {\n\t\tif (con->view->impl->set_resizing) {\n\t\t\tcon->view->impl->set_resizing(con->view, resizing);\n\t\t}\n\t} else {\n\t\tfor (int i = 0; i < con->pending.children->length; ++i ) {\n\t\t\tstruct sway_container *child = con->pending.children->items[i];\n\t\t\tcontainer_set_resizing(child, resizing);\n\t\t}\n\t}\n}\n\nvoid container_set_floating(struct sway_container *container, bool enable) {\n\tif (container_is_floating(container) == enable) {\n\t\treturn;\n\t}\n\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_workspace *workspace = container->pending.workspace;\n\tstruct sway_container *focus = seat_get_focused_container(seat);\n\tbool set_focus = focus == container;\n\n\tif (enable) {\n\t\tstruct sway_container *old_parent = container->pending.parent;\n\t\tcontainer_detach(container);\n\t\tworkspace_add_floating(workspace, container);\n\t\tif (container->view) {\n\t\t\tview_set_tiled(container->view, false);\n\t\t\tif (container->view->using_csd) {\n\t\t\t\tcontainer->saved_border = container->pending.border;\n\t\t\t\tcontainer->pending.border = B_CSD;\n\t\t\t\tif (container->view->xdg_decoration) {\n\t\t\t\t\tstruct sway_xdg_decoration *deco = container->view->xdg_decoration;\n\t\t\t\t\twlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,\n\t\t\t\t\t\t\tWLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcontainer_floating_set_default_size(container);\n\t\tcontainer_floating_resize_and_center(container);\n\t\tif (old_parent) {\n\t\t\tif (set_focus) {\n\t\t\t\tseat_set_raw_focus(seat, &old_parent->node);\n\t\t\t\tseat_set_raw_focus(seat, &container->node);\n\t\t\t}\n\t\t\tcontainer_reap_empty(old_parent);\n\t\t}\n\t} else {\n\t\t// Returning to tiled\n\t\tif (container->scratchpad) {\n\t\t\troot_scratchpad_remove_container(container);\n\t\t}\n\t\tcontainer_detach(container);\n\t\tstruct sway_container *reference =\n\t\t\tseat_get_focus_inactive_tiling(seat, workspace);\n\t\tif (reference) {\n\t\t\tif (reference->view) {\n\t\t\t\tcontainer_add_sibling(reference, container, 1);\n\t\t\t} else {\n\t\t\t\tcontainer_add_child(reference, container);\n\t\t\t}\n\t\t\tcontainer->pending.width = reference->pending.width;\n\t\t\tcontainer->pending.height = reference->pending.height;\n\t\t} else {\n\t\t\tstruct sway_container *other =\n\t\t\t\tworkspace_add_tiling(workspace, container);\n\t\t\tother->pending.width = workspace->width;\n\t\t\tother->pending.height = workspace->height;\n\t\t}\n\t\tif (container->view) {\n\t\t\tview_set_tiled(container->view, true);\n\t\t\tif (container->view->using_csd) {\n\t\t\t\tcontainer->pending.border = container->saved_border;\n\t\t\t\tif (container->view->xdg_decoration) {\n\t\t\t\t\tstruct sway_xdg_decoration *deco = container->view->xdg_decoration;\n\t\t\t\t\twlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,\n\t\t\t\t\t\t\tWLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcontainer->width_fraction = 0;\n\t\tcontainer->height_fraction = 0;\n\t}\n\n\tcontainer_end_mouse_operation(container);\n\n\tipc_event_window(container, \"floating\");\n}\n\nvoid container_set_geometry_from_content(struct sway_container *con) {\n\tif (!sway_assert(con->view, \"Expected a view\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(container_is_floating(con), \"Expected a floating view\")) {\n\t\treturn;\n\t}\n\tsize_t border_width = 0;\n\tsize_t top = 0;\n\n\tif (con->pending.border != B_CSD && !con->pending.fullscreen_mode) {\n\t\tborder_width = con->pending.border_thickness * (con->pending.border != B_NONE);\n\t\ttop = con->pending.border == B_NORMAL ?\n\t\t\tcontainer_titlebar_height() : border_width;\n\t}\n\n\tcon->pending.x = con->pending.content_x - border_width;\n\tcon->pending.y = con->pending.content_y - top;\n\tcon->pending.width = con->pending.content_width + border_width * 2;\n\tcon->pending.height = top + con->pending.content_height + border_width;\n\tnode_set_dirty(&con->node);\n}\n\nbool container_is_floating(struct sway_container *container) {\n\tif (!container->pending.parent && container->pending.workspace &&\n\t\t\tlist_find(container->pending.workspace->floating, container) != -1) {\n\t\treturn true;\n\t}\n\tif (container->scratchpad) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid container_get_box(struct sway_container *container, struct wlr_box *box) {\n\tbox->x = container->pending.x;\n\tbox->y = container->pending.y;\n\tbox->width = container->pending.width;\n\tbox->height = container->pending.height;\n}\n\n/**\n * Translate the container's position as well as all children.\n */\nvoid container_floating_translate(struct sway_container *con,\n\t\tdouble x_amount, double y_amount) {\n\tcon->pending.x += x_amount;\n\tcon->pending.y += y_amount;\n\tcon->pending.content_x += x_amount;\n\tcon->pending.content_y += y_amount;\n\n\tif (con->pending.children) {\n\t\tfor (int i = 0; i < con->pending.children->length; ++i) {\n\t\t\tstruct sway_container *child = con->pending.children->items[i];\n\t\t\tcontainer_floating_translate(child, x_amount, y_amount);\n\t\t}\n\t}\n\n\tnode_set_dirty(&con->node);\n}\n\n/**\n * Choose an output for the floating container's new position.\n *\n * If the center of the container intersects an output then we'll choose that\n * one, otherwise we'll choose whichever output is closest to the container's\n * center.\n */\nstruct sway_output *container_floating_find_output(struct sway_container *con) {\n\tdouble center_x = con->pending.x + con->pending.width / 2;\n\tdouble center_y = con->pending.y + con->pending.height / 2;\n\tstruct sway_output *closest_output = NULL;\n\tdouble closest_distance = DBL_MAX;\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tstruct wlr_box output_box;\n\t\tdouble closest_x, closest_y;\n\t\toutput_get_box(output, &output_box);\n\t\twlr_box_closest_point(&output_box, center_x, center_y,\n\t\t\t\t&closest_x, &closest_y);\n\t\tif (center_x == closest_x && center_y == closest_y) {\n\t\t\t// The center of the floating container is on this output\n\t\t\treturn output;\n\t\t}\n\t\tdouble x_dist = closest_x - center_x;\n\t\tdouble y_dist = closest_y - center_y;\n\t\tdouble distance = x_dist * x_dist + y_dist * y_dist;\n\t\tif (distance < closest_distance) {\n\t\t\tclosest_output = output;\n\t\t\tclosest_distance = distance;\n\t\t}\n\t}\n\treturn closest_output;\n}\n\nvoid container_floating_move_to(struct sway_container *con,\n\t\tdouble lx, double ly) {\n\tif (!sway_assert(container_is_floating(con),\n\t\t\t\"Expected a floating container\")) {\n\t\treturn;\n\t}\n\tcontainer_floating_translate(con, lx - con->pending.x, ly - con->pending.y);\n\tif (container_is_scratchpad_hidden(con)) {\n\t\treturn;\n\t}\n\tstruct sway_workspace *old_workspace = con->pending.workspace;\n\tstruct sway_output *new_output = container_floating_find_output(con);\n\tif (!sway_assert(new_output, \"Unable to find any output\")) {\n\t\treturn;\n\t}\n\tstruct sway_workspace *new_workspace =\n\t\toutput_get_active_workspace(new_output);\n\tif (new_workspace && old_workspace != new_workspace) {\n\t\tcontainer_detach(con);\n\t\tworkspace_add_floating(new_workspace, con);\n\t\tarrange_workspace(old_workspace);\n\t\tarrange_workspace(new_workspace);\n\t\t// If the moved container was a visible scratchpad container, then\n\t\t// update its transform.\n\t\tif (con->scratchpad) {\n\t\t\tstruct wlr_box output_box;\n\t\t\toutput_get_box(new_output, &output_box);\n\t\t\tcon->transform = output_box;\n\t\t}\n\t\tworkspace_detect_urgent(old_workspace);\n\t\tworkspace_detect_urgent(new_workspace);\n\t}\n}\n\nvoid container_floating_move_to_center(struct sway_container *con) {\n\tif (!sway_assert(container_is_floating(con),\n\t\t\t\"Expected a floating container\")) {\n\t\treturn;\n\t}\n\tstruct sway_workspace *ws = con->pending.workspace;\n\tdouble new_lx = ws->x + (ws->width - con->pending.width) / 2;\n\tdouble new_ly = ws->y + (ws->height - con->pending.height) / 2;\n\tcontainer_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y);\n}\n\nstatic bool find_urgent_iterator(struct sway_container *con, void *data) {\n\treturn con->view && view_is_urgent(con->view);\n}\n\nbool container_has_urgent_child(struct sway_container *container) {\n\treturn container_find_child(container, find_urgent_iterator, NULL);\n}\n\nvoid container_end_mouse_operation(struct sway_container *container) {\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseatop_unref(seat, container);\n\t}\n}\n\nstatic void set_fullscreen(struct sway_container *con, bool enable) {\n\tif (!con->view) {\n\t\treturn;\n\t}\n\tif (con->view->impl->set_fullscreen) {\n\t\tcon->view->impl->set_fullscreen(con->view, enable);\n\t\tif (con->view->foreign_toplevel) {\n\t\t\twlr_foreign_toplevel_handle_v1_set_fullscreen(\n\t\t\t\tcon->view->foreign_toplevel, enable);\n\t\t}\n\t}\n}\n\nstatic void container_fullscreen_workspace(struct sway_container *con) {\n\tif (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,\n\t\t\t\t\"Expected a non-fullscreen container\")) {\n\t\treturn;\n\t}\n\tset_fullscreen(con, true);\n\tcon->pending.fullscreen_mode = FULLSCREEN_WORKSPACE;\n\n\tcon->saved_x = con->pending.x;\n\tcon->saved_y = con->pending.y;\n\tcon->saved_width = con->pending.width;\n\tcon->saved_height = con->pending.height;\n\n\tif (con->pending.workspace) {\n\t\tcon->pending.workspace->fullscreen = con;\n\t\tstruct sway_seat *seat;\n\t\tstruct sway_workspace *focus_ws;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tfocus_ws = seat_get_focused_workspace(seat);\n\t\t\tif (focus_ws == con->pending.workspace) {\n\t\t\t\tseat_set_focus_container(seat, con);\n\t\t\t} else {\n\t\t\t\tstruct sway_node *focus =\n\t\t\t\t\tseat_get_focus_inactive(seat, &root->node);\n\t\t\t\tseat_set_raw_focus(seat, &con->node);\n\t\t\t\tseat_set_raw_focus(seat, focus);\n\t\t\t}\n\t\t}\n\t}\n\n\tcontainer_end_mouse_operation(con);\n\tipc_event_window(con, \"fullscreen_mode\");\n}\n\nstatic void container_fullscreen_global(struct sway_container *con) {\n\tif (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,\n\t\t\t\t\"Expected a non-fullscreen container\")) {\n\t\treturn;\n\t}\n\tset_fullscreen(con, true);\n\n\troot->fullscreen_global = con;\n\tcon->saved_x = con->pending.x;\n\tcon->saved_y = con->pending.y;\n\tcon->saved_width = con->pending.width;\n\tcon->saved_height = con->pending.height;\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tstruct sway_container *focus = seat_get_focused_container(seat);\n\t\tif (focus && focus != con) {\n\t\t\tseat_set_focus_container(seat, con);\n\t\t}\n\t}\n\n\tcon->pending.fullscreen_mode = FULLSCREEN_GLOBAL;\n\tcontainer_end_mouse_operation(con);\n\tipc_event_window(con, \"fullscreen_mode\");\n}\n\nvoid container_fullscreen_disable(struct sway_container *con) {\n\tif (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE,\n\t\t\t\t\"Expected a fullscreen container\")) {\n\t\treturn;\n\t}\n\tset_fullscreen(con, false);\n\n\tif (container_is_floating(con)) {\n\t\tcon->pending.x = con->saved_x;\n\t\tcon->pending.y = con->saved_y;\n\t\tcon->pending.width = con->saved_width;\n\t\tcon->pending.height = con->saved_height;\n\t}\n\n\tif (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {\n\t\tif (con->pending.workspace) {\n\t\t\tcon->pending.workspace->fullscreen = NULL;\n\t\t\tif (container_is_floating(con)) {\n\t\t\t\tstruct sway_output *output =\n\t\t\t\t\tcontainer_floating_find_output(con);\n\t\t\t\tif (con->pending.workspace->output != output) {\n\t\t\t\t\tcontainer_floating_move_to_center(con);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\troot->fullscreen_global = NULL;\n\t}\n\n\t// If the container was mapped as fullscreen and set as floating by\n\t// criteria, it needs to be reinitialized as floating to get the proper\n\t// size and location\n\tif (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) {\n\t\tcontainer_floating_resize_and_center(con);\n\t}\n\n\tcon->pending.fullscreen_mode = FULLSCREEN_NONE;\n\tcontainer_end_mouse_operation(con);\n\tipc_event_window(con, \"fullscreen_mode\");\n\n\tif (con->scratchpad) {\n\t\tstruct sway_seat *seat;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tstruct sway_container *focus = seat_get_focused_container(seat);\n\t\t\tif (focus == con || container_has_ancestor(focus, con)) {\n\t\t\t\tseat_set_focus(seat,\n\t\t\t\t\t\tseat_get_focus_inactive(seat, &root->node));\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid container_set_fullscreen(struct sway_container *con,\n\t\tenum sway_fullscreen_mode mode) {\n\tif (con->pending.fullscreen_mode == mode) {\n\t\treturn;\n\t}\n\n\tswitch (mode) {\n\tcase FULLSCREEN_NONE:\n\t\tcontainer_fullscreen_disable(con);\n\t\tbreak;\n\tcase FULLSCREEN_WORKSPACE:\n\t\tif (root->fullscreen_global) {\n\t\t\tcontainer_fullscreen_disable(root->fullscreen_global);\n\t\t}\n\t\tif (con->pending.workspace && con->pending.workspace->fullscreen) {\n\t\t\tcontainer_fullscreen_disable(con->pending.workspace->fullscreen);\n\t\t}\n\t\tcontainer_fullscreen_workspace(con);\n\t\tbreak;\n\tcase FULLSCREEN_GLOBAL:\n\t\tif (root->fullscreen_global) {\n\t\t\tcontainer_fullscreen_disable(root->fullscreen_global);\n\t\t}\n\t\tif (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {\n\t\t\tcontainer_fullscreen_disable(con);\n\t\t}\n\t\tcontainer_fullscreen_global(con);\n\t\tbreak;\n\t}\n}\n\nstruct sway_container *container_toplevel_ancestor(\n\t\tstruct sway_container *container) {\n\twhile (container->pending.parent) {\n\t\tcontainer = container->pending.parent;\n\t}\n\n\treturn container;\n}\n\nbool container_is_floating_or_child(struct sway_container *container) {\n\treturn container_is_floating(container_toplevel_ancestor(container));\n}\n\nbool container_is_fullscreen_or_child(struct sway_container *container) {\n\tdo {\n\t\tif (container->pending.fullscreen_mode) {\n\t\t\treturn true;\n\t\t}\n\t\tcontainer = container->pending.parent;\n\t} while (container);\n\n\treturn false;\n}\n\nenum sway_container_layout container_parent_layout(struct sway_container *con) {\n\tif (con->pending.parent) {\n\t\treturn con->pending.parent->pending.layout;\n\t}\n\tif (con->pending.workspace) {\n\t\treturn con->pending.workspace->layout;\n\t}\n\treturn L_NONE;\n}\n\nlist_t *container_get_siblings(struct sway_container *container) {\n\tif (container->pending.parent) {\n\t\treturn container->pending.parent->pending.children;\n\t}\n\tif (!container->pending.workspace) {\n\t\treturn NULL;\n\t}\n\tif (list_find(container->pending.workspace->tiling, container) != -1) {\n\t\treturn container->pending.workspace->tiling;\n\t}\n\treturn container->pending.workspace->floating;\n}\n\nint container_sibling_index(struct sway_container *child) {\n\treturn list_find(container_get_siblings(child), child);\n}\n\nvoid container_handle_fullscreen_reparent(struct sway_container *con) {\n\tif (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||\n\t\t\tcon->pending.workspace->fullscreen == con) {\n\t\treturn;\n\t}\n\tif (con->pending.workspace->fullscreen) {\n\t\tcontainer_fullscreen_disable(con->pending.workspace->fullscreen);\n\t}\n\tcon->pending.workspace->fullscreen = con;\n\n\tarrange_workspace(con->pending.workspace);\n}\n\nstatic void set_workspace(struct sway_container *container, void *data) {\n\tcontainer->pending.workspace = container->pending.parent->pending.workspace;\n}\n\nvoid container_insert_child(struct sway_container *parent,\n\t\tstruct sway_container *child, int i) {\n\tif (child->pending.workspace) {\n\t\tcontainer_detach(child);\n\t}\n\tlist_insert(parent->pending.children, i, child);\n\tchild->pending.parent = parent;\n\tchild->pending.workspace = parent->pending.workspace;\n\tcontainer_for_each_child(child, set_workspace, NULL);\n\tcontainer_handle_fullscreen_reparent(child);\n\tcontainer_update_representation(parent);\n}\n\nvoid container_add_sibling(struct sway_container *fixed,\n\t\tstruct sway_container *active, bool after) {\n\tif (active->pending.workspace) {\n\t\tcontainer_detach(active);\n\t}\n\tlist_t *siblings = container_get_siblings(fixed);\n\tint index = list_find(siblings, fixed);\n\tlist_insert(siblings, index + after, active);\n\tactive->pending.parent = fixed->pending.parent;\n\tactive->pending.workspace = fixed->pending.workspace;\n\tcontainer_for_each_child(active, set_workspace, NULL);\n\tcontainer_handle_fullscreen_reparent(active);\n\tcontainer_update_representation(active);\n}\n\nvoid container_add_child(struct sway_container *parent,\n\t\tstruct sway_container *child) {\n\tif (child->pending.workspace) {\n\t\tcontainer_detach(child);\n\t}\n\tlist_add(parent->pending.children, child);\n\tchild->pending.parent = parent;\n\tchild->pending.workspace = parent->pending.workspace;\n\tcontainer_for_each_child(child, set_workspace, NULL);\n\tcontainer_handle_fullscreen_reparent(child);\n\tcontainer_update_representation(parent);\n\tnode_set_dirty(&child->node);\n\tnode_set_dirty(&parent->node);\n}\n\nvoid container_detach(struct sway_container *child) {\n\tif (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {\n\t\tchild->pending.workspace->fullscreen = NULL;\n\t}\n\tif (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\troot->fullscreen_global = NULL;\n\t}\n\n\tstruct sway_container *old_parent = child->pending.parent;\n\tstruct sway_workspace *old_workspace = child->pending.workspace;\n\tlist_t *siblings = container_get_siblings(child);\n\tif (siblings) {\n\t\tint index = list_find(siblings, child);\n\t\tif (index != -1) {\n\t\t\tlist_del(siblings, index);\n\t\t}\n\t}\n\tchild->pending.parent = NULL;\n\tchild->pending.workspace = NULL;\n\tcontainer_for_each_child(child, set_workspace, NULL);\n\n\tif (old_parent) {\n\t\tcontainer_update_representation(old_parent);\n\t\tnode_set_dirty(&old_parent->node);\n\t} else if (old_workspace) {\n\t\tworkspace_update_representation(old_workspace);\n\t\tnode_set_dirty(&old_workspace->node);\n\t}\n\tnode_set_dirty(&child->node);\n}\n\nvoid container_replace(struct sway_container *container,\n\t\tstruct sway_container *replacement) {\n\tenum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode;\n\tbool scratchpad = container->scratchpad;\n\tstruct sway_workspace *ws = NULL;\n\tif (fullscreen != FULLSCREEN_NONE) {\n\t\tcontainer_fullscreen_disable(container);\n\t}\n\tif (scratchpad) {\n\t\tws = container->pending.workspace;\n\t\troot_scratchpad_show(container);\n\t\troot_scratchpad_remove_container(container);\n\t}\n\tif (container->pending.parent || container->pending.workspace) {\n\t\tfloat width_fraction = container->width_fraction;\n\t\tfloat height_fraction = container->height_fraction;\n\t\tcontainer_add_sibling(container, replacement, 1);\n\t\tcontainer_detach(container);\n\t\treplacement->width_fraction = width_fraction;\n\t\treplacement->height_fraction = height_fraction;\n\t}\n\tif (scratchpad) {\n\t\troot_scratchpad_add_container(replacement, ws);\n\t}\n\tswitch (fullscreen) {\n\tcase FULLSCREEN_WORKSPACE:\n\t\tcontainer_fullscreen_workspace(replacement);\n\t\tbreak;\n\tcase FULLSCREEN_GLOBAL:\n\t\tcontainer_fullscreen_global(replacement);\n\t\tbreak;\n\tcase FULLSCREEN_NONE:\n\t\t// noop\n\t\tbreak;\n\t}\n}\n\nstruct sway_container *container_split(struct sway_container *child,\n\t\tenum sway_container_layout layout) {\n\t// i3 doesn't split singleton H/V containers\n\t// https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354\n\tif (child->pending.parent || child->pending.workspace) {\n\t\tlist_t *siblings = container_get_siblings(child);\n\t\tif (siblings->length == 1) {\n\t\t\tenum sway_container_layout current = container_parent_layout(child);\n\t\t\tif (container_is_floating(child)) {\n\t\t\t\tcurrent = L_NONE;\n\t\t\t}\n\t\t\tif (current == L_HORIZ || current == L_VERT) {\n\t\t\t\tif (child->pending.parent) {\n\t\t\t\t\tchild->pending.parent->pending.layout = layout;\n\t\t\t\t\tcontainer_update_representation(child->pending.parent);\n\t\t\t\t} else {\n\t\t\t\t\tchild->pending.workspace->layout = layout;\n\t\t\t\t\tworkspace_update_representation(child->pending.workspace);\n\t\t\t\t}\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t}\n\n\tstruct sway_seat *seat = input_manager_get_default_seat();\n\tbool set_focus = (seat_get_focus(seat) == &child->node);\n\n\tif (container_is_floating(child) && child->view) {\n\t\tview_set_tiled(child->view, true);\n\t\tif (child->view->using_csd) {\n\t\t\tchild->pending.border = child->saved_border;\n\t\t}\n\t}\n\n\tstruct sway_container *cont = container_create(NULL);\n\tcont->pending.width = child->pending.width;\n\tcont->pending.height = child->pending.height;\n\tcont->width_fraction = child->width_fraction;\n\tcont->height_fraction = child->height_fraction;\n\tcont->pending.x = child->pending.x;\n\tcont->pending.y = child->pending.y;\n\tcont->pending.layout = layout;\n\n\tcontainer_replace(child, cont);\n\tcontainer_add_child(cont, child);\n\n\tif (set_focus) {\n\t\tseat_set_raw_focus(seat, &cont->node);\n\t\tif (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\t\tseat_set_focus(seat, &child->node);\n\t\t} else {\n\t\t\tseat_set_raw_focus(seat, &child->node);\n\t\t}\n\t}\n\n\treturn cont;\n}\n\nbool container_is_transient_for(struct sway_container *child,\n\t\tstruct sway_container *ancestor) {\n\treturn config->popup_during_fullscreen == POPUP_SMART &&\n\t\tchild->view && ancestor->view &&\n\t\tview_is_transient_for(child->view, ancestor->view);\n}\n\nstatic bool find_by_mark_iterator(struct sway_container *con, void *data) {\n\tchar *mark = data;\n\treturn container_has_mark(con, mark);\n}\n\nstruct sway_container *container_find_mark(char *mark) {\n\treturn root_find_container(find_by_mark_iterator, mark);\n}\n\nbool container_find_and_unmark(char *mark) {\n\tstruct sway_container *con = root_find_container(\n\t\tfind_by_mark_iterator, mark);\n\tif (!con) {\n\t\treturn false;\n\t}\n\n\tfor (int i = 0; i < con->marks->length; ++i) {\n\t\tchar *con_mark = con->marks->items[i];\n\t\tif (strcmp(con_mark, mark) == 0) {\n\t\t\tfree(con_mark);\n\t\t\tlist_del(con->marks, i);\n\t\t\tcontainer_update_marks(con);\n\t\t\tipc_event_window(con, \"mark\");\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid container_clear_marks(struct sway_container *con) {\n\tfor (int i = 0; i < con->marks->length; ++i) {\n\t\tfree(con->marks->items[i]);\n\t}\n\tcon->marks->length = 0;\n\tipc_event_window(con, \"mark\");\n}\n\nbool container_has_mark(struct sway_container *con, char *mark) {\n\tfor (int i = 0; i < con->marks->length; ++i) {\n\t\tchar *item = con->marks->items[i];\n\t\tif (strcmp(item, mark) == 0) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid container_add_mark(struct sway_container *con, char *mark) {\n\tlist_add(con->marks, strdup(mark));\n\tipc_event_window(con, \"mark\");\n}\n\nvoid container_raise_floating(struct sway_container *con) {\n\t// Bring container to front by putting it at the end of the floating list.\n\tstruct sway_container *floater = container_toplevel_ancestor(con);\n\tif (container_is_floating(floater) && floater->pending.workspace) {\n\t\t// it's okay to just raise the scene directly instead of waiting\n\t\t// for the transaction to go through. We won't be reconfiguring\n\t\t// surfaces\n\t\twlr_scene_node_raise_to_top(&floater->scene_tree->node);\n\n\t\tlist_move_to_end(floater->pending.workspace->floating, floater);\n\t\tnode_set_dirty(&floater->pending.workspace->node);\n\t}\n}\n\nbool container_is_scratchpad_hidden(struct sway_container *con) {\n\treturn con->scratchpad && !con->pending.workspace;\n}\n\nbool container_is_scratchpad_hidden_or_child(struct sway_container *con) {\n\tcon = container_toplevel_ancestor(con);\n\treturn con->scratchpad && !con->pending.workspace;\n}\n\nbool container_is_sticky(struct sway_container *con) {\n\treturn con->is_sticky && container_is_floating(con);\n}\n\nbool container_is_sticky_or_child(struct sway_container *con) {\n\treturn container_is_sticky(container_toplevel_ancestor(con));\n}\n\nstatic bool is_parallel(enum sway_container_layout first,\n\t\tenum sway_container_layout second) {\n\tswitch (first) {\n\tcase L_TABBED:\n\tcase L_HORIZ:\n\t\treturn second == L_TABBED || second == L_HORIZ;\n\tcase L_STACKED:\n\tcase L_VERT:\n\t\treturn second == L_STACKED || second == L_VERT;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nstatic bool container_is_squashable(struct sway_container *con,\n\t\tstruct sway_container *child) {\n\tenum sway_container_layout gp_layout = container_parent_layout(con);\n\treturn (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) &&\n\t\t(child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) &&\n\t\t!is_parallel(con->pending.layout, child->pending.layout) &&\n\t\tis_parallel(gp_layout, child->pending.layout);\n}\n\nstatic void container_squash_children(struct sway_container *con) {\n\tfor (int i = 0; i < con->pending.children->length; i++) {\n\t\tstruct sway_container *child = con->pending.children->items[i];\n\t\ti += container_squash(child);\n\t}\n}\n\nint container_squash(struct sway_container *con) {\n\tif (!con->pending.children) {\n\t\treturn 0;\n\t}\n\tif (con->pending.children->length != 1) {\n\t\tcontainer_squash_children(con);\n\t\treturn 0;\n\t}\n\tstruct sway_container *child = con->pending.children->items[0];\n\tint idx = container_sibling_index(con);\n\tint change = 0;\n\tif (container_is_squashable(con, child)) {\n\t\t// con and child are a redundant H/V pair. Destroy them.\n\t\twhile (child->pending.children->length) {\n\t\t\tstruct sway_container *current = child->pending.children->items[0];\n\t\t\tcontainer_detach(current);\n\t\t\tif (con->pending.parent) {\n\t\t\t\tcontainer_insert_child(con->pending.parent, current, idx);\n\t\t\t} else {\n\t\t\t\tworkspace_insert_tiling_direct(con->pending.workspace, current, idx);\n\t\t\t}\n\t\t\tchange++;\n\t\t}\n\t\t// This will also destroy con because child was its only child\n\t\tcontainer_reap_empty(child);\n\t\tchange--;\n\t} else {\n\t\tcontainer_squash_children(con);\n\t}\n\treturn change;\n}\n\nstatic void swap_places(struct sway_container *con1,\n\t\tstruct sway_container *con2) {\n\tstruct sway_container *temp = malloc(sizeof(struct sway_container));\n\ttemp->pending.x = con1->pending.x;\n\ttemp->pending.y = con1->pending.y;\n\ttemp->pending.width = con1->pending.width;\n\ttemp->pending.height = con1->pending.height;\n\ttemp->width_fraction = con1->width_fraction;\n\ttemp->height_fraction = con1->height_fraction;\n\ttemp->pending.parent = con1->pending.parent;\n\ttemp->pending.workspace = con1->pending.workspace;\n\tbool temp_floating = container_is_floating(con1);\n\n\tcon1->pending.x = con2->pending.x;\n\tcon1->pending.y = con2->pending.y;\n\tcon1->pending.width = con2->pending.width;\n\tcon1->pending.height = con2->pending.height;\n\tcon1->width_fraction = con2->width_fraction;\n\tcon1->height_fraction = con2->height_fraction;\n\n\tcon2->pending.x = temp->pending.x;\n\tcon2->pending.y = temp->pending.y;\n\tcon2->pending.width = temp->pending.width;\n\tcon2->pending.height = temp->pending.height;\n\tcon2->width_fraction = temp->width_fraction;\n\tcon2->height_fraction = temp->height_fraction;\n\n\tint temp_index = container_sibling_index(con1);\n\tif (con2->pending.parent) {\n\t\tcontainer_insert_child(con2->pending.parent, con1,\n\t\t\t\tcontainer_sibling_index(con2));\n\t} else if (container_is_floating(con2)) {\n\t\tworkspace_add_floating(con2->pending.workspace, con1);\n\t} else {\n\t\tworkspace_insert_tiling(con2->pending.workspace, con1,\n\t\t\t\tcontainer_sibling_index(con2));\n\t}\n\tif (temp->pending.parent) {\n\t\tcontainer_insert_child(temp->pending.parent, con2, temp_index);\n\t} else if (temp_floating) {\n\t\tworkspace_add_floating(temp->pending.workspace, con2);\n\t} else {\n\t\tworkspace_insert_tiling(temp->pending.workspace, con2, temp_index);\n\t}\n\n\tfree(temp);\n}\n\nstatic void swap_focus(struct sway_container *con1,\n\t\tstruct sway_container *con2, struct sway_seat *seat,\n\t\tstruct sway_container *focus) {\n\tif (focus == con1 || focus == con2) {\n\t\tstruct sway_workspace *ws1 = con1->pending.workspace;\n\t\tstruct sway_workspace *ws2 = con2->pending.workspace;\n\t\tenum sway_container_layout layout1 = container_parent_layout(con1);\n\t\tenum sway_container_layout layout2 = container_parent_layout(con2);\n\t\tif (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {\n\t\t\tif (workspace_is_visible(ws2)) {\n\t\t\t\tseat_set_focus(seat, &con2->node);\n\t\t\t}\n\t\t\tseat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);\n\t\t} else if (focus == con2 && (layout1 == L_TABBED\n\t\t\t\t\t|| layout1 == L_STACKED)) {\n\t\t\tif (workspace_is_visible(ws1)) {\n\t\t\t\tseat_set_focus(seat, &con1->node);\n\t\t\t}\n\t\t\tseat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);\n\t\t} else if (ws1 != ws2) {\n\t\t\tseat_set_focus_container(seat, focus == con1 ? con2 : con1);\n\t\t} else {\n\t\t\tseat_set_focus_container(seat, focus);\n\t\t}\n\t} else {\n\t\tseat_set_focus_container(seat, focus);\n\t}\n\n\tif (root->fullscreen_global) {\n\t\tseat_set_focus(seat,\n\t\t\t\tseat_get_focus_inactive(seat, &root->fullscreen_global->node));\n\t}\n}\n\nvoid container_swap(struct sway_container *con1, struct sway_container *con2) {\n\tif (!sway_assert(con1 && con2, \"Cannot swap with nothing\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(!container_has_ancestor(con1, con2)\n\t\t\t\t&& !container_has_ancestor(con2, con1),\n\t\t\t\t\"Cannot swap ancestor and descendant\")) {\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Swapping containers %zu and %zu\",\n\t\t\tcon1->node.id, con2->node.id);\n\n\tbool scratch1 = con1->scratchpad;\n\tbool hidden1 = container_is_scratchpad_hidden(con1);\n\tbool scratch2 = con2->scratchpad;\n\tbool hidden2 = container_is_scratchpad_hidden(con2);\n\tif (scratch1) {\n\t\tif (hidden1) {\n\t\t\troot_scratchpad_show(con1);\n\t\t}\n\t\troot_scratchpad_remove_container(con1);\n\t}\n\tif (scratch2) {\n\t\tif (hidden2) {\n\t\t\troot_scratchpad_show(con2);\n\t\t}\n\t\troot_scratchpad_remove_container(con2);\n\t}\n\n\tenum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;\n\tif (fs1) {\n\t\tcontainer_fullscreen_disable(con1);\n\t}\n\tenum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;\n\tif (fs2) {\n\t\tcontainer_fullscreen_disable(con2);\n\t}\n\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_container *focus = seat_get_focused_container(seat);\n\tstruct sway_workspace *vis1 =\n\t\toutput_get_active_workspace(con1->pending.workspace->output);\n\tstruct sway_workspace *vis2 =\n\t\toutput_get_active_workspace(con2->pending.workspace->output);\n\tif (!sway_assert(vis1 && vis2, \"con1 or con2 are on an output without a\"\n\t\t\t\t\"workspace. This should not happen\")) {\n\t\treturn;\n\t}\n\n\tchar *stored_prev_name = NULL;\n\tif (seat->prev_workspace_name) {\n\t\tstored_prev_name = strdup(seat->prev_workspace_name);\n\t}\n\n\tswap_places(con1, con2);\n\n\tif (!workspace_is_visible(vis1)) {\n\t\tseat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));\n\t}\n\tif (!workspace_is_visible(vis2)) {\n\t\tseat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));\n\t}\n\n\tswap_focus(con1, con2, seat, focus);\n\n\tif (stored_prev_name) {\n\t\tfree(seat->prev_workspace_name);\n\t\tseat->prev_workspace_name = stored_prev_name;\n\t}\n\n\tif (scratch1) {\n\t\troot_scratchpad_add_container(con2, NULL);\n\t\tif (!hidden1) {\n\t\t\troot_scratchpad_show(con2);\n\t\t}\n\t}\n\tif (scratch2) {\n\t\troot_scratchpad_add_container(con1, NULL);\n\t\tif (!hidden2) {\n\t\t\troot_scratchpad_show(con1);\n\t\t}\n\t}\n\n\tif (fs1) {\n\t\tcontainer_set_fullscreen(con2, fs1);\n\t}\n\tif (fs2) {\n\t\tcontainer_set_fullscreen(con1, fs2);\n\t}\n}\n"
  },
  {
    "path": "sway/tree/node.c",
    "content": "#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/node.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/workspace.h\"\n#include \"log.h\"\n\nvoid node_init(struct sway_node *node, enum sway_node_type type, void *thing) {\n\tstatic size_t next_id = 1;\n\tnode->id = next_id++;\n\tnode->type = type;\n\tnode->sway_root = thing;\n\twl_signal_init(&node->events.destroy);\n}\n\nconst char *node_type_to_str(enum sway_node_type type) {\n\tswitch (type) {\n\tcase N_ROOT:\n\t\treturn \"root\";\n\tcase N_OUTPUT:\n\t\treturn \"output\";\n\tcase N_WORKSPACE:\n\t\treturn \"workspace\";\n\tcase N_CONTAINER:\n\t\treturn \"container\";\n\t}\n\treturn \"\";\n}\n\nvoid node_set_dirty(struct sway_node *node) {\n\tif (node->dirty || node->destroying) {\n\t\treturn;\n\t}\n\tnode->dirty = true;\n\tlist_add(server.dirty_nodes, node);\n}\n\nbool node_is_view(struct sway_node *node) {\n\treturn node->type == N_CONTAINER && node->sway_container->view;\n}\n\nchar *node_get_name(struct sway_node *node) {\n\tswitch (node->type) {\n\tcase N_ROOT:\n\t\treturn \"root\";\n\tcase N_OUTPUT:\n\t\treturn node->sway_output->wlr_output->name;\n\tcase N_WORKSPACE:\n\t\treturn node->sway_workspace->name;\n\tcase N_CONTAINER:\n\t\treturn node->sway_container->title;\n\t}\n\treturn NULL;\n}\n\nvoid node_get_box(struct sway_node *node, struct wlr_box *box) {\n\tswitch (node->type) {\n\tcase N_ROOT:\n\t\troot_get_box(root, box);\n\t\tbreak;\n\tcase N_OUTPUT:\n\t\toutput_get_box(node->sway_output, box);\n\t\tbreak;\n\tcase N_WORKSPACE:\n\t\tworkspace_get_box(node->sway_workspace, box);\n\t\tbreak;\n\tcase N_CONTAINER:\n\t\tcontainer_get_box(node->sway_container, box);\n\t\tbreak;\n\t}\n}\n\nstruct sway_output *node_get_output(struct sway_node *node) {\n\tswitch (node->type) {\n\tcase N_CONTAINER: {\n\t\tstruct sway_workspace *ws = node->sway_container->pending.workspace;\n\t\treturn ws ? ws->output : NULL;\n    }\n\tcase N_WORKSPACE:\n\t\treturn node->sway_workspace->output;\n\tcase N_OUTPUT:\n\t\treturn node->sway_output;\n\tcase N_ROOT:\n\t\treturn NULL;\n\t}\n\treturn NULL;\n}\n\nenum sway_container_layout node_get_layout(struct sway_node *node) {\n\tswitch (node->type) {\n\tcase N_CONTAINER:\n\t\treturn node->sway_container->pending.layout;\n\tcase N_WORKSPACE:\n\t\treturn node->sway_workspace->layout;\n\tcase N_OUTPUT:\n\tcase N_ROOT:\n\t\treturn L_NONE;\n\t}\n\treturn L_NONE;\n}\n\nstruct sway_node *node_get_parent(struct sway_node *node) {\n\tswitch (node->type) {\n\tcase N_CONTAINER: {\n\t\t\tstruct sway_container *con = node->sway_container;\n\t\t\tif (con->pending.parent) {\n\t\t\t\treturn &con->pending.parent->node;\n\t\t\t}\n\t\t\tif (con->pending.workspace) {\n\t\t\t\treturn &con->pending.workspace->node;\n\t\t\t}\n\t\t}\n\t\treturn NULL;\n\tcase N_WORKSPACE: {\n\t\t\tstruct sway_workspace *ws = node->sway_workspace;\n\t\t\tif (ws->output) {\n\t\t\t\treturn &ws->output->node;\n\t\t\t}\n\t\t}\n\t\treturn NULL;\n\tcase N_OUTPUT:\n\t\treturn &root->node;\n\tcase N_ROOT:\n\t\treturn NULL;\n\t}\n\treturn NULL;\n}\n\nlist_t *node_get_children(struct sway_node *node) {\n\tswitch (node->type) {\n\tcase N_CONTAINER:\n\t\treturn node->sway_container->pending.children;\n\tcase N_WORKSPACE:\n\t\treturn node->sway_workspace->tiling;\n\tcase N_OUTPUT:\n\tcase N_ROOT:\n\t\treturn NULL;\n\t}\n\treturn NULL;\n}\n\nbool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {\n\tif (ancestor->type == N_ROOT && node->type == N_CONTAINER &&\n\t\t\tnode->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\treturn true;\n\t}\n\tstruct sway_node *parent = node_get_parent(node);\n\twhile (parent) {\n\t\tif (parent == ancestor) {\n\t\t\treturn true;\n\t\t}\n\t\tif (ancestor->type == N_ROOT && parent->type == N_CONTAINER &&\n\t\t\t\tparent->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\t\treturn true;\n\t\t}\n\t\tparent = node_get_parent(parent);\n\t}\n\treturn false;\n}\n\nvoid scene_node_disown_children(struct wlr_scene_tree *tree) {\n\t// this function can be called as part of destruction code that will be invoked\n\t// upon an allocation failure. Let's not crash on NULL due to an allocation error.\n\tif (!tree) {\n\t\treturn;\n\t}\n\n\tstruct wlr_scene_node *child, *tmp_child;\n\twl_list_for_each_safe(child, tmp_child, &tree->children, link) {\n\t\twlr_scene_node_reparent(child, root->staging);\n\t}\n}\n\nstruct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent,\n\t\tbool *failed) {\n\t// fallthrough\n\tif (*failed) {\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_scene_tree *tree = wlr_scene_tree_create(parent);\n\tif (!tree) {\n\t\tsway_log(SWAY_ERROR, \"Failed to allocate a scene node\");\n\t\t*failed = true;\n\t}\n\n\treturn tree;\n}\n"
  },
  {
    "path": "sway/tree/output.c",
    "content": "#include <assert.h>\n#include <ctype.h>\n#include <string.h>\n#include <strings.h>\n#include \"sway/ipc-server.h\"\n#include \"sway/layers.h\"\n#include \"sway/output.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/server.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nenum wlr_direction opposite_direction(enum wlr_direction d) {\n\tswitch (d) {\n\tcase WLR_DIRECTION_UP:\n\t\treturn WLR_DIRECTION_DOWN;\n\tcase WLR_DIRECTION_DOWN:\n\t\treturn WLR_DIRECTION_UP;\n\tcase WLR_DIRECTION_RIGHT:\n\t\treturn WLR_DIRECTION_LEFT;\n\tcase WLR_DIRECTION_LEFT:\n\t\treturn WLR_DIRECTION_RIGHT;\n\t}\n\tassert(false);\n\treturn 0;\n}\n\nstatic void restore_workspaces(struct sway_output *output) {\n\t// Workspace output priority\n\tfor (int i = 0; i < root->outputs->length; i++) {\n\t\tstruct sway_output *other = root->outputs->items[i];\n\t\tif (other == output) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (int j = 0; j < other->workspaces->length; j++) {\n\t\t\tstruct sway_workspace *ws = other->workspaces->items[j];\n\t\t\tstruct sway_output *highest =\n\t\t\t\tworkspace_output_get_highest_available(ws);\n\t\t\tif (highest == output) {\n\t\t\t\tworkspace_detach(ws);\n\t\t\t\toutput_add_workspace(output, ws);\n\t\t\t\tipc_event_workspace(NULL, ws, \"move\");\n\t\t\t\tj--;\n\t\t\t}\n\t\t}\n\n\t\tif (other->workspaces->length == 0) {\n\t\t\tchar *next = workspace_next_name(other->wlr_output->name);\n\t\t\tstruct sway_workspace *ws = workspace_create(other, next);\n\t\t\tfree(next);\n\t\t\tipc_event_workspace(NULL, ws, \"init\");\n\t\t}\n\t}\n\n\t// Saved workspaces\n\twhile (root->fallback_output->workspaces->length) {\n\t\tstruct sway_workspace *ws = root->fallback_output->workspaces->items[0];\n\t\tworkspace_detach(ws);\n\t\toutput_add_workspace(output, ws);\n\n\t\t// If the floater was made floating while on the NOOP output, its width\n\t\t// and height will be zero and it should be reinitialized as a floating\n\t\t// container to get the appropriate size and location. Additionally, if\n\t\t// the floater is wider or taller than the output or is completely\n\t\t// outside of the output's bounds, do the same as the output layout has\n\t\t// likely changed and the maximum size needs to be checked and the\n\t\t// floater re-centered\n\t\tfor (int i = 0; i < ws->floating->length; i++) {\n\t\t\tstruct sway_container *floater = ws->floating->items[i];\n\t\t\tif (floater->pending.width == 0 || floater->pending.height == 0 ||\n\t\t\t\t\tfloater->pending.width > output->width ||\n\t\t\t\t\tfloater->pending.height > output->height ||\n\t\t\t\t\tfloater->pending.x > output->lx + output->width ||\n\t\t\t\t\tfloater->pending.y > output->ly + output->height ||\n\t\t\t\t\tfloater->pending.x + floater->pending.width < output->lx ||\n\t\t\t\t\tfloater->pending.y + floater->pending.height < output->ly) {\n\t\t\t\tcontainer_floating_resize_and_center(floater);\n\t\t\t}\n\t\t}\n\n\t\tipc_event_workspace(NULL, ws, \"move\");\n\t}\n\n\toutput_sort_workspaces(output);\n}\n\nstatic void destroy_scene_layers(struct sway_output *output) {\n\twlr_scene_node_destroy(&output->fullscreen_background->node);\n\n\tscene_node_disown_children(output->layers.tiling);\n\tscene_node_disown_children(output->layers.fullscreen);\n\n\twlr_scene_node_destroy(&output->layers.shell_background->node);\n\twlr_scene_node_destroy(&output->layers.shell_bottom->node);\n\twlr_scene_node_destroy(&output->layers.tiling->node);\n\twlr_scene_node_destroy(&output->layers.fullscreen->node);\n\twlr_scene_node_destroy(&output->layers.shell_top->node);\n\twlr_scene_node_destroy(&output->layers.shell_overlay->node);\n\twlr_scene_node_destroy(&output->layers.session_lock->node);\n}\n\nstruct sway_output *output_create(struct wlr_output *wlr_output) {\n\tstruct sway_output *output = calloc(1, sizeof(struct sway_output));\n\tnode_init(&output->node, N_OUTPUT, output);\n\n\tbool failed = false;\n\toutput->layers.shell_background = alloc_scene_tree(root->staging, &failed);\n\toutput->layers.shell_bottom = alloc_scene_tree(root->staging, &failed);\n\toutput->layers.tiling = alloc_scene_tree(root->staging, &failed);\n\toutput->layers.fullscreen = alloc_scene_tree(root->staging, &failed);\n\toutput->layers.shell_top = alloc_scene_tree(root->staging, &failed);\n\toutput->layers.shell_overlay = alloc_scene_tree(root->staging, &failed);\n\toutput->layers.session_lock = alloc_scene_tree(root->staging, &failed);\n\n\tif (!failed) {\n\t\toutput->fullscreen_background = wlr_scene_rect_create(\n\t\t\toutput->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});\n\n\t\tif (!output->fullscreen_background) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to allocate a background rect\");\n\t\t\tfailed = true;\n\t\t}\n\t}\n\n\tif (failed) {\n\t\tdestroy_scene_layers(output);\n\t\twlr_scene_output_destroy(output->scene_output);\n\t\tfree(output);\n\t\treturn NULL;\n\t}\n\n\toutput->wlr_output = wlr_output;\n\twlr_output->data = output;\n\toutput->detected_subpixel = wlr_output->subpixel;\n\toutput->scale_filter = SCALE_FILTER_NEAREST;\n\n\twl_list_insert(&root->all_outputs, &output->link);\n\n\toutput->workspaces = create_list();\n\toutput->current.workspaces = create_list();\n\twl_list_init(&output->layer_surfaces);\n\n\treturn output;\n}\n\nvoid output_enable(struct sway_output *output) {\n\tif (!sway_assert(!output->enabled, \"output is already enabled\")) {\n\t\treturn;\n\t}\n\tstruct wlr_output *wlr_output = output->wlr_output;\n\toutput->enabled = true;\n\tlist_add(root->outputs, output);\n\n\trestore_workspaces(output);\n\n\tstruct sway_workspace *ws = NULL;\n\tif (!output->workspaces->length) {\n\t\t// Create workspace\n\t\tchar *ws_name = workspace_next_name(wlr_output->name);\n\t\tsway_log(SWAY_DEBUG, \"Creating default workspace %s\", ws_name);\n\t\tws = workspace_create(output, ws_name);\n\t\t// Set each seat's focus if not already set\n\t\tstruct sway_seat *seat = NULL;\n\t\twl_list_for_each(seat, &server.input->seats, link) {\n\t\t\tif (!seat->has_focus) {\n\t\t\t\tseat_set_focus_workspace(seat, ws);\n\t\t\t}\n\t\t}\n\t\tfree(ws_name);\n\t\tipc_event_workspace(NULL, ws, \"init\");\n\t}\n\n\tif (ws && config->default_orientation == L_NONE) {\n\t\t// Since the output transformation and resolution could have changed\n\t\t// due to applying the output config, the previously set layout for the\n\t\t// created workspace may not be correct for `default_orientation auto`\n\t\tws->layout = output_get_default_layout(output);\n\t}\n\n\twl_signal_emit_mutable(&root->events.new_node, &output->node);\n}\n\nstatic void evacuate_sticky(struct sway_workspace *old_ws,\n\t\tstruct sway_output *new_output) {\n\tstruct sway_workspace *new_ws = output_get_active_workspace(new_output);\n\tif (!sway_assert(new_ws, \"New output does not have a workspace\")) {\n\t\treturn;\n\t}\n\twhile(old_ws->floating->length) {\n\t\tstruct sway_container *sticky = old_ws->floating->items[0];\n\t\tcontainer_detach(sticky);\n\t\tworkspace_add_floating(new_ws, sticky);\n\t\tcontainer_handle_fullscreen_reparent(sticky);\n\t\tcontainer_floating_move_to_center(sticky);\n\t\tipc_event_window(sticky, \"move\");\n\t}\n\tworkspace_detect_urgent(new_ws);\n}\n\nstatic void output_evacuate(struct sway_output *output) {\n\tif (!output->workspaces->length) {\n\t\treturn;\n\t}\n\tstruct sway_output *fallback_output = NULL;\n\tif (root->outputs->length > 0) {\n\t\tfallback_output = root->outputs->items[0];\n\t}\n\n\twhile (output->workspaces->length) {\n\t\tstruct sway_workspace *workspace = output->workspaces->items[0];\n\n\t\tworkspace_detach(workspace);\n\n\t\tstruct sway_output *new_output =\n\t\t\tworkspace_output_get_highest_available(workspace);\n\t\tif (!new_output) {\n\t\t\tnew_output = fallback_output;\n\t\t}\n\t\tif (!new_output) {\n\t\t\tnew_output = root->fallback_output;\n\t\t}\n\n\t\tstruct sway_workspace *new_output_ws =\n\t\t\toutput_get_active_workspace(new_output);\n\n\t\tif (workspace_is_empty(workspace)) {\n\t\t\t// If the new output has an active workspace (the noop output may\n\t\t\t// not have one), move all sticky containers to it\n\t\t\tif (new_output_ws &&\n\t\t\t\t\tworkspace_num_sticky_containers(workspace) > 0) {\n\t\t\t\tevacuate_sticky(workspace, new_output);\n\t\t\t}\n\n\t\t\tif (workspace_num_sticky_containers(workspace) == 0) {\n\t\t\t\tworkspace_begin_destroy(workspace);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tworkspace_output_add_priority(workspace, new_output);\n\t\toutput_add_workspace(new_output, workspace);\n\t\toutput_sort_workspaces(new_output);\n\t\tipc_event_workspace(NULL, workspace, \"move\");\n\n\t\t// If there is an old workspace (the noop output may not have one),\n\t\t// check to see if it is empty and should be destroyed.\n\t\tif (new_output_ws) {\n\t\t\tworkspace_consider_destroy(new_output_ws);\n\t\t}\n\t}\n}\n\nvoid output_destroy(struct sway_output *output) {\n\tif (!sway_assert(output->node.destroying,\n\t\t\t\t\"Tried to free output which wasn't marked as destroying\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(output->wlr_output == NULL,\n\t\t\t\t\"Tried to free output which still had a wlr_output\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(output->node.ntxnrefs == 0, \"Tried to free output \"\n\t\t\t\t\"which is still referenced by transactions\")) {\n\t\treturn;\n\t}\n\n\tdestroy_scene_layers(output);\n\tlist_free(output->workspaces);\n\tlist_free(output->current.workspaces);\n\twlr_color_transform_unref(output->color_transform);\n\tfree(output);\n}\n\nvoid output_disable(struct sway_output *output) {\n\tif (!sway_assert(output->enabled, \"Expected an enabled output\")) {\n\t\treturn;\n\t}\n\tint index = list_find(root->outputs, output);\n\tif (!sway_assert(index >= 0, \"Output not found in root node\")) {\n\t\treturn;\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Disabling output '%s'\", output->wlr_output->name);\n\n\t// Remove the output now to avoid interacting with it during e.g.,\n\t// transactions, as the output might be physically removed with the scene\n\t// output destroyed.\n\tlist_del(root->outputs, index);\n\toutput->enabled = false;\n\n\tdestroy_layers(output);\n\toutput_evacuate(output);\n}\n\nvoid output_begin_destroy(struct sway_output *output) {\n\tif (!sway_assert(!output->enabled, \"Expected a disabled output\")) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Destroying output '%s'\", output->wlr_output->name);\n\twl_signal_emit_mutable(&output->node.events.destroy, &output->node);\n\n\tnode_set_dirty(&output->node);\n\toutput->node.destroying = true;\n}\n\nstruct sway_output *output_from_wlr_output(struct wlr_output *output) {\n\treturn output->data;\n}\n\nstruct sway_output *output_get_in_direction(struct sway_output *reference,\n\t\tenum wlr_direction direction) {\n\tif (!sway_assert(direction, \"got invalid direction: %d\", direction)) {\n\t\treturn NULL;\n\t}\n\tstruct wlr_box output_box;\n\twlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box);\n\tint lx = output_box.x + output_box.width / 2;\n\tint ly = output_box.y + output_box.height / 2;\n\tstruct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(\n\t\t\troot->output_layout, direction, reference->wlr_output, lx, ly);\n\tif (!wlr_adjacent) {\n\t\treturn NULL;\n\t}\n\treturn output_from_wlr_output(wlr_adjacent);\n}\n\nvoid output_add_workspace(struct sway_output *output,\n\t\tstruct sway_workspace *workspace) {\n\tif (workspace->output) {\n\t\tworkspace_detach(workspace);\n\t}\n\tlist_add(output->workspaces, workspace);\n\tworkspace->output = output;\n\tnode_set_dirty(&output->node);\n\tnode_set_dirty(&workspace->node);\n}\n\nvoid output_for_each_workspace(struct sway_output *output,\n\t\tvoid (*f)(struct sway_workspace *ws, void *data), void *data) {\n\tfor (int i = 0; i < output->workspaces->length; ++i) {\n\t\tstruct sway_workspace *workspace = output->workspaces->items[i];\n\t\tf(workspace, data);\n\t}\n}\n\nvoid output_for_each_container(struct sway_output *output,\n\t\tvoid (*f)(struct sway_container *con, void *data), void *data) {\n\tfor (int i = 0; i < output->workspaces->length; ++i) {\n\t\tstruct sway_workspace *workspace = output->workspaces->items[i];\n\t\tworkspace_for_each_container(workspace, f, data);\n\t}\n}\n\nstruct sway_workspace *output_find_workspace(struct sway_output *output,\n\t\tbool (*test)(struct sway_workspace *ws, void *data), void *data) {\n\tfor (int i = 0; i < output->workspaces->length; ++i) {\n\t\tstruct sway_workspace *workspace = output->workspaces->items[i];\n\t\tif (test(workspace, data)) {\n\t\t\treturn workspace;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_container *output_find_container(struct sway_output *output,\n\t\tbool (*test)(struct sway_container *con, void *data), void *data) {\n\tstruct sway_container *result = NULL;\n\tfor (int i = 0; i < output->workspaces->length; ++i) {\n\t\tstruct sway_workspace *workspace = output->workspaces->items[i];\n\t\tif ((result = workspace_find_container(workspace, test, data))) {\n\t\t\treturn result;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic int sort_workspace_cmp_qsort(const void *_a, const void *_b) {\n\tstruct sway_workspace *a = *(void **)_a;\n\tstruct sway_workspace *b = *(void **)_b;\n\n\tif (isdigit(a->name[0]) && isdigit(b->name[0])) {\n\t\tint a_num = strtol(a->name, NULL, 10);\n\t\tint b_num = strtol(b->name, NULL, 10);\n\t\treturn (a_num < b_num) ? -1 : (a_num > b_num);\n\t} else if (isdigit(a->name[0])) {\n\t\treturn -1;\n\t} else if (isdigit(b->name[0])) {\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid output_sort_workspaces(struct sway_output *output) {\n\tlist_stable_sort(output->workspaces, sort_workspace_cmp_qsort);\n}\n\nvoid output_get_box(struct sway_output *output, struct wlr_box *box) {\n\tbox->x = output->lx;\n\tbox->y = output->ly;\n\tbox->width = output->width;\n\tbox->height = output->height;\n}\n\nstatic void handle_destroy_non_desktop(struct wl_listener *listener, void *data) {\n\tstruct sway_output_non_desktop *output =\n\t\twl_container_of(listener, output, destroy);\n\n\tsway_log(SWAY_DEBUG, \"Destroying non-desktop output '%s'\", output->wlr_output->name);\n\n\tint index = list_find(root->non_desktop_outputs, output);\n\tlist_del(root->non_desktop_outputs, index);\n\n\twl_list_remove(&output->destroy.link);\n\n\tfree(output);\n}\n\nstruct sway_output_non_desktop *output_non_desktop_create(\n\t\tstruct wlr_output *wlr_output) {\n\tstruct sway_output_non_desktop *output =\n\t\tcalloc(1, sizeof(struct sway_output_non_desktop));\n\n\toutput->wlr_output = wlr_output;\n\n\twl_signal_add(&wlr_output->events.destroy, &output->destroy);\n\toutput->destroy.notify = handle_destroy_non_desktop;\n\n\treturn output;\n}\n\nenum sway_container_layout output_get_default_layout(\n\t\tstruct sway_output *output) {\n\tif (config->default_orientation != L_NONE) {\n\t\treturn config->default_orientation;\n\t}\n\tif (output->height > output->width) {\n\t\treturn L_VERT;\n\t}\n\treturn L_HORIZ;\n}\n"
  },
  {
    "path": "sway/tree/root.c",
    "content": "#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_scene.h>\n#include <wlr/util/transform.h>\n#include \"sway/desktop/transaction.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/root.h\"\n#include \"sway/tree/workspace.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct sway_root *root;\n\nstruct sway_root *root_create(struct wl_display *wl_display) {\n\tstruct sway_root *root = calloc(1, sizeof(struct sway_root));\n\tif (!root) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate sway_root\");\n\t\treturn NULL;\n\t}\n\n\tstruct wlr_scene *root_scene = wlr_scene_create();\n\tif (!root_scene) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate root scene node\");\n\t\tfree(root);\n\t\treturn NULL;\n\t}\n\n\tnode_init(&root->node, N_ROOT, root);\n\troot->root_scene = root_scene;\n\n\tbool failed = false;\n\troot->staging = alloc_scene_tree(&root_scene->tree, &failed);\n\troot->layer_tree = alloc_scene_tree(&root_scene->tree, &failed);\n\n\troot->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.tiling = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.floating = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed);\n#if WLR_HAS_XWAYLAND\n\troot->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed);\n#endif\n\troot->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.popup = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.seat = alloc_scene_tree(root->layer_tree, &failed);\n\troot->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed);\n\n\tif (!failed && !scene_descriptor_assign(&root->layers.seat->node,\n\t\t\tSWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) {\n\t\tfailed = true;\n\t}\n\n\tif (failed) {\n\t\twlr_scene_node_destroy(&root_scene->tree.node);\n\t\tfree(root);\n\t\treturn NULL;\n\t}\n\n\twlr_scene_node_set_enabled(&root->staging->node, false);\n\n\troot->output_layout = wlr_output_layout_create(wl_display);\n\twl_list_init(&root->all_outputs);\n\twl_signal_init(&root->events.new_node);\n\troot->outputs = create_list();\n\troot->non_desktop_outputs = create_list();\n\troot->scratchpad = create_list();\n\n\treturn root;\n}\n\nvoid root_destroy(struct sway_root *root) {\n\tlist_free(root->scratchpad);\n\tlist_free(root->non_desktop_outputs);\n\tlist_free(root->outputs);\n\twlr_scene_node_destroy(&root->root_scene->tree.node);\n\tfree(root);\n}\n\nstatic void set_container_transform(struct sway_workspace *ws,\n\t\t\tstruct sway_container *con) {\n\tstruct sway_output *output = ws->output;\n\tstruct wlr_box box = {0};\n\tif (output) {\n\t\toutput_get_box(output, &box);\n\t}\n\tcon->transform = box;\n}\n\nvoid root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) {\n\tif (!sway_assert(!con->scratchpad, \"Container is already in scratchpad\")) {\n\t\treturn;\n\t}\n\n\tstruct sway_container *parent = con->pending.parent;\n\tstruct sway_workspace *workspace = con->pending.workspace;\n\n\tset_container_transform(workspace, con);\n\n\t// Clear the fullscreen mode when sending to the scratchpad\n\tif (con->pending.fullscreen_mode != FULLSCREEN_NONE) {\n\t\tcontainer_fullscreen_disable(con);\n\t}\n\n\t// When a tiled window is sent to scratchpad, center and resize it.\n\tif (!container_is_floating(con)) {\n\t\tcontainer_set_floating(con, true);\n\t\tcontainer_floating_set_default_size(con);\n\t\tcontainer_floating_move_to_center(con);\n\t}\n\n\tcontainer_detach(con);\n\tcon->scratchpad = true;\n\tlist_add(root->scratchpad, con);\n\tif (ws) {\n\t\tworkspace_add_floating(ws, con);\n\t}\n\n\tif (!ws) {\n\t\tstruct sway_seat *seat = input_manager_current_seat();\n\t\tstruct sway_node *new_focus = NULL;\n\t\tif (parent) {\n\t\t\tarrange_container(parent);\n\t\t\tnew_focus = seat_get_focus_inactive(seat, &parent->node);\n\t\t}\n\t\tif (!new_focus) {\n\t\t\tarrange_workspace(workspace);\n\t\t\tnew_focus = seat_get_focus_inactive(seat, &workspace->node);\n\t\t}\n\t\tseat_set_focus(seat, new_focus);\n\t}\n\n\tipc_event_window(con, \"move\");\n}\n\nvoid root_scratchpad_remove_container(struct sway_container *con) {\n\tif (!sway_assert(con->scratchpad, \"Container is not in scratchpad\")) {\n\t\treturn;\n\t}\n\tcon->scratchpad = false;\n\tint index = list_find(root->scratchpad, con);\n\tif (index != -1) {\n\t\tlist_del(root->scratchpad, index);\n\t\tipc_event_window(con, \"move\");\n\t}\n}\n\nvoid root_scratchpad_show(struct sway_container *con) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_workspace *new_ws = seat_get_focused_workspace(seat);\n\tif (!new_ws) {\n\t\tsway_log(SWAY_DEBUG, \"No focused workspace to show scratchpad on\");\n\t\treturn;\n\t}\n\tstruct sway_workspace *old_ws = con->pending.workspace;\n\n\t// If the current con or any of its parents are in fullscreen mode, we\n\t// first need to disable it before showing the scratchpad con.\n\tif (new_ws->fullscreen) {\n\t\tcontainer_fullscreen_disable(new_ws->fullscreen);\n\t}\n\tif (root->fullscreen_global) {\n\t\tcontainer_fullscreen_disable(root->fullscreen_global);\n\t}\n\n\t// Show the container\n\tif (old_ws) {\n\t\tcontainer_detach(con);\n\t\t// Make sure the last inactive container on the old workspace is above\n\t\t// the workspace itself in the focus stack.\n\t\tstruct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node);\n\t\tseat_set_raw_focus(seat, node);\n\t} else {\n\t\t// Act on the ancestor of scratchpad hidden split containers\n\t\twhile (con->pending.parent) {\n\t\t\tcon = con->pending.parent;\n\t\t}\n\t}\n\tworkspace_add_floating(new_ws, con);\n\n\tif (new_ws->output) {\n\t\tstruct wlr_box output_box;\n\t\toutput_get_box(new_ws->output, &output_box);\n\t\tfloating_fix_coordinates(con, &con->transform, &output_box);\n\t}\n\tset_container_transform(new_ws, con);\n\n\tarrange_workspace(new_ws);\n\tseat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));\n\tif (old_ws) {\n\t\tworkspace_consider_destroy(old_ws);\n\t}\n\n\tcontainer_raise_floating(con);\n}\n\nstatic void disable_fullscreen(struct sway_container *con, void *data) {\n\tif (con->pending.fullscreen_mode != FULLSCREEN_NONE) {\n\t\tcontainer_fullscreen_disable(con);\n\t}\n}\n\nvoid root_scratchpad_hide(struct sway_container *con) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_node *focus = seat_get_focus_inactive(seat, &root->node);\n\tstruct sway_workspace *ws = con->pending.workspace;\n\n\tif (!con->pending.workspace) {\n\t\treturn;\n\t}\n\n\tset_container_transform(con->pending.workspace, con);\n\n\tdisable_fullscreen(con, NULL);\n\tcontainer_for_each_child(con, disable_fullscreen, NULL);\n\tcontainer_detach(con);\n\tarrange_workspace(ws);\n\tif (&con->node == focus || node_has_ancestor(focus, &con->node)) {\n\t\tseat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));\n\t}\n\tlist_move_to_end(root->scratchpad, con);\n\n\tipc_event_window(con, \"move\");\n}\n\nvoid root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),\n\t\tvoid *data) {\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\toutput_for_each_workspace(output, f, data);\n\t}\n}\n\nvoid root_for_each_container(void (*f)(struct sway_container *con, void *data),\n\t\tvoid *data) {\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\toutput_for_each_container(output, f, data);\n\t}\n\n\t// Scratchpad\n\tfor (int i = 0; i < root->scratchpad->length; ++i) {\n\t\tstruct sway_container *container = root->scratchpad->items[i];\n\t\tif (container_is_scratchpad_hidden(container)) {\n\t\t\tf(container, data);\n\t\t\tcontainer_for_each_child(container, f, data);\n\t\t}\n\t}\n\n\t// Saved workspaces\n\tfor (int i = 0; i < root->fallback_output->workspaces->length; ++i) {\n\t\tstruct sway_workspace *ws = root->fallback_output->workspaces->items[i];\n\t\tworkspace_for_each_container(ws, f, data);\n\t}\n}\n\nstruct sway_output *root_find_output(\n\t\tbool (*test)(struct sway_output *output, void *data), void *data) {\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tif (test(output, data)) {\n\t\t\treturn output;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_workspace *root_find_workspace(\n\t\tbool (*test)(struct sway_workspace *ws, void *data), void *data) {\n\tstruct sway_workspace *result = NULL;\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tif ((result = output_find_workspace(output, test, data))) {\n\t\t\treturn result;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_container *root_find_container(\n\t\tbool (*test)(struct sway_container *con, void *data), void *data) {\n\tstruct sway_container *result = NULL;\n\tfor (int i = 0; i < root->outputs->length; ++i) {\n\t\tstruct sway_output *output = root->outputs->items[i];\n\t\tif ((result = output_find_container(output, test, data))) {\n\t\t\treturn result;\n\t\t}\n\t}\n\n\t// Scratchpad\n\tfor (int i = 0; i < root->scratchpad->length; ++i) {\n\t\tstruct sway_container *container = root->scratchpad->items[i];\n\t\tif (container_is_scratchpad_hidden(container)) {\n\t\t\tif (test(container, data)) {\n\t\t\t\treturn container;\n\t\t\t}\n\t\t\tif ((result = container_find_child(container, test, data))) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Saved workspaces\n\tfor (int i = 0; i < root->fallback_output->workspaces->length; ++i) {\n\t\tstruct sway_workspace *ws = root->fallback_output->workspaces->items[i];\n\t\tif ((result = workspace_find_container(ws, test, data))) {\n\t\t\treturn result;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nvoid root_get_box(struct sway_root *root, struct wlr_box *box) {\n\tbox->x = root->x;\n\tbox->y = root->y;\n\tbox->width = root->width;\n\tbox->height = root->height;\n}\n"
  },
  {
    "path": "sway/tree/view.c",
    "content": "#include <stdlib.h>\n#include <strings.h>\n#include <wayland-server-core.h>\n#include <wlr/config.h>\n#include <wlr/render/wlr_renderer.h>\n#include <wlr/types/wlr_buffer.h>\n#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>\n#include <wlr/types/wlr_foreign_toplevel_management_v1.h>\n#include <wlr/types/wlr_fractional_scale_v1.h>\n#include <wlr/types/wlr_output_layout.h>\n#include <wlr/types/wlr_security_context_v1.h>\n#include <wlr/types/wlr_server_decoration.h>\n#include <wlr/types/wlr_subcompositor.h>\n#include <wlr/types/wlr_xdg_decoration_v1.h>\n#include <wlr/types/wlr_session_lock_v1.h>\n#if WLR_HAS_XWAYLAND\n#include <wlr/xwayland.h>\n#endif\n#include \"list.h\"\n#include \"log.h\"\n#include \"sway/criteria.h\"\n#include \"sway/commands.h\"\n#include \"sway/desktop/transaction.h\"\n#include \"sway/desktop/idle_inhibit_v1.h\"\n#include \"sway/desktop/launcher.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/scene_descriptor.h\"\n#include \"sway/server.h\"\n#include \"sway/sway_text_node.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"sway/config.h\"\n#include \"sway/xdg_decoration.h\"\n#include \"stringop.h\"\n\nbool view_init(struct sway_view *view, enum sway_view_type type,\n\t\tconst struct sway_view_impl *impl) {\n\tbool failed = false;\n\tview->scene_tree = alloc_scene_tree(root->staging, &failed);\n\tview->content_tree = alloc_scene_tree(view->scene_tree, &failed);\n\tif (failed) {\n\t\tgoto err;\n\t}\n\n\tif (!scene_descriptor_assign(&view->scene_tree->node, SWAY_SCENE_DESC_VIEW, view)) {\n\t\tgoto err;\n\t}\n\n\tview->image_capture_scene = wlr_scene_create();\n\tif (view->image_capture_scene == NULL) {\n\t\tgoto err;\n\t}\n\tview->image_capture_scene->restack_xwayland_surfaces = false;\n\n\tview->type = type;\n\tview->impl = impl;\n\tview->executed_criteria = create_list();\n\tview->allow_request_urgent = true;\n\tview->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;\n\tview->tearing_mode = TEARING_WINDOW_HINT;\n\twl_signal_init(&view->events.unmap);\n\treturn true;\n\nerr:\n\twlr_scene_node_destroy(&view->scene_tree->node);\n\treturn false;\n}\n\nvoid view_destroy(struct sway_view *view) {\n\tif (!sway_assert(view->surface == NULL, \"Tried to free mapped view\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(view->destroying,\n\t\t\t\t\"Tried to free view which wasn't marked as destroying\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(view->container == NULL,\n\t\t\t\t\"Tried to free view which still has a container \"\n\t\t\t\t\"(might have a pending transaction?)\")) {\n\t\treturn;\n\t}\n\twl_list_remove(&view->events.unmap.listener_list);\n\tlist_free(view->executed_criteria);\n\n\tview_assign_ctx(view, NULL);\n\twlr_scene_node_destroy(&view->image_capture_scene->tree.node);\n\twlr_scene_node_destroy(&view->scene_tree->node);\n\tif (view->impl->destroy) {\n\t\tview->impl->destroy(view);\n\t} else {\n\t\tfree(view);\n\t}\n}\n\nvoid view_begin_destroy(struct sway_view *view) {\n\tif (!sway_assert(view->surface == NULL, \"Tried to destroy a mapped view\")) {\n\t\treturn;\n\t}\n\tview->destroying = true;\n\n\tif (!view->container) {\n\t\tview_destroy(view);\n\t}\n}\n\nconst char *view_get_title(struct sway_view *view) {\n\tif (view->impl->get_string_prop) {\n\t\treturn view->impl->get_string_prop(view, VIEW_PROP_TITLE);\n\t}\n\treturn NULL;\n}\n\nconst char *view_get_app_id(struct sway_view *view) {\n\tif (view->impl->get_string_prop) {\n\t\treturn view->impl->get_string_prop(view, VIEW_PROP_APP_ID);\n\t}\n\treturn NULL;\n}\n\nconst char *view_get_class(struct sway_view *view) {\n\tif (view->impl->get_string_prop) {\n\t\treturn view->impl->get_string_prop(view, VIEW_PROP_CLASS);\n\t}\n\treturn NULL;\n}\n\nconst char *view_get_instance(struct sway_view *view) {\n\tif (view->impl->get_string_prop) {\n\t\treturn view->impl->get_string_prop(view, VIEW_PROP_INSTANCE);\n\t}\n\treturn NULL;\n}\n#if WLR_HAS_XWAYLAND\nuint32_t view_get_x11_window_id(struct sway_view *view) {\n\tif (view->impl->get_int_prop) {\n\t\treturn view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);\n\t}\n\treturn 0;\n}\n\nuint32_t view_get_x11_parent_id(struct sway_view *view) {\n\tif (view->impl->get_int_prop) {\n\t\treturn view->impl->get_int_prop(view, VIEW_PROP_X11_PARENT_ID);\n\t}\n\treturn 0;\n}\n#endif\nconst char *view_get_window_role(struct sway_view *view) {\n\tif (view->impl->get_string_prop) {\n\t\treturn view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE);\n\t}\n\treturn NULL;\n}\n\nuint32_t view_get_window_type(struct sway_view *view) {\n\tif (view->impl->get_int_prop) {\n\t\treturn view->impl->get_int_prop(view, VIEW_PROP_WINDOW_TYPE);\n\t}\n\treturn 0;\n}\n\nstatic const struct wlr_security_context_v1_state *security_context_from_view(\n\t\tstruct sway_view *view) {\n\tconst struct wl_client *client =\n\t\twl_resource_get_client(view->surface->resource);\n\tconst struct wlr_security_context_v1_state *security_context =\n\t\twlr_security_context_manager_v1_lookup_client(\n\t\t\t\tserver.security_context_manager_v1, client);\n\treturn security_context;\n}\n\nconst char *view_get_sandbox_engine(struct sway_view *view) {\n\tconst struct wlr_security_context_v1_state *security_context =\n\t\tsecurity_context_from_view(view);\n\treturn security_context ? security_context->sandbox_engine : NULL;\n}\n\nconst char *view_get_sandbox_app_id(struct sway_view *view) {\n\tconst struct wlr_security_context_v1_state *security_context =\n\t\tsecurity_context_from_view(view);\n\treturn security_context ? security_context->app_id : NULL;\n}\n\nconst char *view_get_sandbox_instance_id(struct sway_view *view) {\n\tconst struct wlr_security_context_v1_state *security_context =\n\t\tsecurity_context_from_view(view);\n\treturn security_context ? security_context->instance_id : NULL;\n}\n\nconst char *view_get_tag(struct sway_view *view) {\n\tif (view->impl->get_string_prop) {\n\t\treturn view->impl->get_string_prop(view, VIEW_PROP_TAG);\n\t}\n\treturn NULL;\n}\n\nconst char *view_get_shell(struct sway_view *view) {\n\tswitch(view->type) {\n\tcase SWAY_VIEW_XDG_SHELL:\n\t\treturn \"xdg_shell\";\n#if WLR_HAS_XWAYLAND\n\tcase SWAY_VIEW_XWAYLAND:\n\t\treturn \"xwayland\";\n#endif\n\t}\n\treturn \"unknown\";\n}\n\nvoid view_get_constraints(struct sway_view *view, double *min_width,\n\t\tdouble *max_width, double *min_height, double *max_height) {\n\tif (view->impl->get_constraints) {\n\t\tview->impl->get_constraints(view,\n\t\t\t\tmin_width, max_width, min_height, max_height);\n\t} else {\n\t\t*min_width = 1;\n\t\t*max_width = DBL_MAX;\n\t\t*min_height = 1;\n\t\t*max_height = DBL_MAX;\n\t}\n}\n\nuint32_t view_configure(struct sway_view *view, double lx, double ly, int width,\n\t\tint height) {\n\tif (view->impl->configure) {\n\t\treturn view->impl->configure(view, lx, ly, width, height);\n\t}\n\treturn 0;\n}\n\nbool view_inhibit_idle(struct sway_view *view) {\n\tif (server.session_lock.lock) {\n\t\treturn false;\n\t}\n\n\tstruct sway_idle_inhibitor_v1 *user_inhibitor =\n\t\tsway_idle_inhibit_v1_user_inhibitor_for_view(view);\n\n\tstruct sway_idle_inhibitor_v1 *application_inhibitor =\n\t\tsway_idle_inhibit_v1_application_inhibitor_for_view(view);\n\n\tif (!user_inhibitor && !application_inhibitor) {\n\t\treturn false;\n\t}\n\n\tif (!user_inhibitor) {\n\t\treturn sway_idle_inhibit_v1_is_active(application_inhibitor);\n\t}\n\n\tif (!application_inhibitor) {\n\t\treturn sway_idle_inhibit_v1_is_active(user_inhibitor);\n\t}\n\n\treturn sway_idle_inhibit_v1_is_active(user_inhibitor)\n\t\t|| sway_idle_inhibit_v1_is_active(application_inhibitor);\n}\n\nbool view_ancestor_is_only_visible(struct sway_view *view) {\n\tbool only_visible = true;\n\tstruct sway_container *con = view->container;\n\twhile (con) {\n\t\tenum sway_container_layout layout = container_parent_layout(con);\n\t\tif (layout != L_TABBED && layout != L_STACKED) {\n\t\t\tlist_t *siblings = container_get_siblings(con);\n\t\t\tif (siblings && siblings->length > 1) {\n\t\t\t\tonly_visible = false;\n\t\t\t}\n\t\t} else {\n\t\t\tonly_visible = true;\n\t\t}\n\t\tcon = con->pending.parent;\n\t}\n\treturn only_visible;\n}\n\nstatic bool view_is_only_visible(struct sway_view *view) {\n\tstruct sway_container *con = view->container;\n\twhile (con) {\n\t\tenum sway_container_layout layout = container_parent_layout(con);\n\t\tif (layout != L_TABBED && layout != L_STACKED) {\n\t\t\tlist_t *siblings = container_get_siblings(con);\n\t\t\tif (siblings && siblings->length > 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tcon = con->pending.parent;\n\t}\n\n\treturn true;\n}\n\nstatic bool gaps_to_edge(struct sway_view *view) {\n\tstruct side_gaps gaps = view->container->pending.workspace->current_gaps;\n\treturn gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0;\n}\n\nvoid view_autoconfigure(struct sway_view *view) {\n\tstruct sway_container *con = view->container;\n\tstruct sway_workspace *ws = con->pending.workspace;\n\n\tif (container_is_scratchpad_hidden(con) &&\n\t\t\tcon->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {\n\t\treturn;\n\t}\n\tstruct sway_output *output = ws ? ws->output : NULL;\n\n\tif (output && con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {\n\t\tcon->pending.content_x = output->lx;\n\t\tcon->pending.content_y = output->ly;\n\t\tcon->pending.content_width = output->width;\n\t\tcon->pending.content_height = output->height;\n\t\treturn;\n\t} else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\tcon->pending.content_x = root->x;\n\t\tcon->pending.content_y = root->y;\n\t\tcon->pending.content_width = root->width;\n\t\tcon->pending.content_height = root->height;\n\t\treturn;\n\t}\n\n\tcon->pending.border_top = con->pending.border_bottom = true;\n\tcon->pending.border_left = con->pending.border_right = true;\n\tdouble y_offset = 0;\n\n\tif (!container_is_floating_or_child(con) && ws) {\n\t\tif (config->hide_edge_borders == E_BOTH\n\t\t\t\t|| config->hide_edge_borders == E_VERTICAL) {\n\t\t\tcon->pending.border_left = con->pending.x != ws->x;\n\t\t\tint right_x = con->pending.x + con->pending.width;\n\t\t\tcon->pending.border_right = right_x != ws->x + ws->width;\n\t\t}\n\n\t\tif (config->hide_edge_borders == E_BOTH\n\t\t\t\t|| config->hide_edge_borders == E_HORIZONTAL) {\n\t\t\tcon->pending.border_top = con->pending.y != ws->y;\n\t\t\tint bottom_y = con->pending.y + con->pending.height;\n\t\t\tcon->pending.border_bottom = bottom_y != ws->y + ws->height;\n\t\t}\n\n\t\tbool smart = config->hide_edge_borders_smart == ESMART_ON ||\n\t\t\t(config->hide_edge_borders_smart == ESMART_NO_GAPS &&\n\t\t\t!gaps_to_edge(view));\n\t\tif (smart) {\n\t\t\tbool show_border = !view_is_only_visible(view);\n\t\t\tcon->pending.border_left &= show_border;\n\t\t\tcon->pending.border_right &= show_border;\n\t\t\tcon->pending.border_top &= show_border;\n\t\t\tcon->pending.border_bottom &= show_border;\n\t\t}\n\t}\n\n\tif (!container_is_floating(con)) {\n\t\t// In a tabbed or stacked container, the container's y is the top of the\n\t\t// title area. We have to offset the surface y by the height of the title,\n\t\t// bar, and disable any top border because we'll always have the title bar.\n\t\tlist_t *siblings = container_get_siblings(con);\n\t\tbool show_titlebar = (siblings && siblings->length > 1)\n\t\t\t|| !config->hide_lone_tab;\n\t\tif (show_titlebar) {\n\t\t\tenum sway_container_layout layout = container_parent_layout(con);\n\t\t\tif (layout == L_TABBED) {\n\t\t\t\ty_offset = container_titlebar_height();\n\t\t\t\tcon->pending.border_top = false;\n\t\t\t} else if (layout == L_STACKED) {\n\t\t\t\ty_offset = container_titlebar_height() * siblings->length;\n\t\t\t\tcon->pending.border_top = false;\n\t\t\t}\n\t\t}\n\t}\n\n\tdouble x, y, width, height;\n\tswitch (con->pending.border) {\n\tdefault:\n\tcase B_CSD:\n\tcase B_NONE:\n\t\tx = con->pending.x;\n\t\ty = con->pending.y + y_offset;\n\t\twidth = con->pending.width;\n\t\theight = con->pending.height - y_offset;\n\t\tbreak;\n\tcase B_PIXEL:\n\t\tx = con->pending.x + con->pending.border_thickness * con->pending.border_left;\n\t\ty = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset;\n\t\twidth = con->pending.width\n\t\t\t- con->pending.border_thickness * con->pending.border_left\n\t\t\t- con->pending.border_thickness * con->pending.border_right;\n\t\theight = con->pending.height - y_offset\n\t\t\t- con->pending.border_thickness * con->pending.border_top\n\t\t\t- con->pending.border_thickness * con->pending.border_bottom;\n\t\tbreak;\n\tcase B_NORMAL:\n\t\t// Height is: 1px border + 3px pad + title height + 3px pad + 1px border\n\t\tx = con->pending.x + con->pending.border_thickness * con->pending.border_left;\n\t\twidth = con->pending.width\n\t\t\t- con->pending.border_thickness * con->pending.border_left\n\t\t\t- con->pending.border_thickness * con->pending.border_right;\n\t\tif (y_offset) {\n\t\t\ty = con->pending.y + y_offset;\n\t\t\theight = con->pending.height - y_offset\n\t\t\t\t- con->pending.border_thickness * con->pending.border_bottom;\n\t\t} else {\n\t\t\ty = con->pending.y + container_titlebar_height();\n\t\t\theight = con->pending.height - container_titlebar_height()\n\t\t\t\t- con->pending.border_thickness * con->pending.border_bottom;\n\t\t}\n\t\tbreak;\n\t}\n\n\tcon->pending.content_x = x;\n\tcon->pending.content_y = y;\n\tcon->pending.content_width = fmax(width, 1);\n\tcon->pending.content_height = fmax(height, 1);\n}\n\nvoid view_set_activated(struct sway_view *view, bool activated) {\n\tif (view->impl->set_activated) {\n\t\tview->impl->set_activated(view, activated);\n\t}\n\tif (view->foreign_toplevel) {\n\t\twlr_foreign_toplevel_handle_v1_set_activated(\n\t\t\t\tview->foreign_toplevel, activated);\n\t}\n}\n\nvoid view_request_activate(struct sway_view *view, struct sway_seat *seat) {\n\tstruct sway_workspace *ws = view->container->pending.workspace;\n\tif (!seat) {\n\t\tseat = input_manager_current_seat();\n\t}\n\n\tswitch (config->focus_on_window_activation) {\n\tcase FOWA_SMART:\n\t\tif (ws && workspace_is_visible(ws)) {\n\t\t\tseat_set_focus_container(seat, view->container);\n\t\t\tcontainer_raise_floating(view->container);\n\t\t} else {\n\t\t\tview_set_urgent(view, true);\n\t\t}\n\t\tbreak;\n\tcase FOWA_URGENT:\n\t\tview_set_urgent(view, true);\n\t\tbreak;\n\tcase FOWA_FOCUS:\n\t\tif (container_is_scratchpad_hidden_or_child(view->container)) {\n\t\t\troot_scratchpad_show(view->container);\n\t\t} else {\n\t\t\tseat_set_focus_container(seat, view->container);\n\t\t\tcontainer_raise_floating(view->container);\n\t\t}\n\t\tbreak;\n\tcase FOWA_NONE:\n\t\tbreak;\n\t}\n\ttransaction_commit_dirty();\n}\n\nvoid view_request_urgent(struct sway_view *view) {\n\tif (config->focus_on_window_activation != FOWA_NONE) {\n\t\tview_set_urgent(view, true);\n\t}\n}\n\nvoid view_set_csd_from_server(struct sway_view *view, bool enabled) {\n\tsway_log(SWAY_DEBUG, \"Telling view %p to set CSD to %i\", view, enabled);\n\tif (view->xdg_decoration) {\n\t\tuint32_t mode = enabled ?\n\t\t\tWLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :\n\t\t\tWLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;\n\t\twlr_xdg_toplevel_decoration_v1_set_mode(\n\t\t\t\tview->xdg_decoration->wlr_xdg_decoration, mode);\n\t}\n\tview->using_csd = enabled;\n}\n\nvoid view_update_csd_from_client(struct sway_view *view, bool enabled) {\n\tsway_log(SWAY_DEBUG, \"View %p updated CSD to %i\", view, enabled);\n\tstruct sway_container *con = view->container;\n\tif (enabled && con && con->pending.border != B_CSD) {\n\t\tcon->saved_border = con->pending.border;\n\t\tif (container_is_floating(con)) {\n\t\t\tcon->pending.border = B_CSD;\n\t\t}\n\t} else if (!enabled && con && con->pending.border == B_CSD) {\n\t\tcon->pending.border = con->saved_border;\n\t}\n\tview->using_csd = enabled;\n}\n\nvoid view_set_tiled(struct sway_view *view, bool tiled) {\n\tif (view->impl->set_tiled) {\n\t\tview->impl->set_tiled(view, tiled);\n\t}\n}\n\nvoid view_close(struct sway_view *view) {\n\tif (view->impl->close) {\n\t\tview->impl->close(view);\n\t}\n}\n\nvoid view_close_popups(struct sway_view *view) {\n\tif (view->impl->close_popups) {\n\t\tview->impl->close_popups(view);\n\t}\n}\n\nstatic bool view_has_executed_criteria(struct sway_view *view,\n\t\tstruct criteria *criteria) {\n\tfor (int i = 0; i < view->executed_criteria->length; ++i) {\n\t\tstruct criteria *item = view->executed_criteria->items[i];\n\t\tif (item == criteria) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid view_execute_criteria(struct sway_view *view) {\n\tlist_t *criterias = criteria_for_view(view, CT_COMMAND);\n\tfor (int i = 0; i < criterias->length; i++) {\n\t\tstruct criteria *criteria = criterias->items[i];\n\t\tsway_log(SWAY_DEBUG, \"Checking criteria %s\", criteria->raw);\n\t\tif (view_has_executed_criteria(view, criteria)) {\n\t\t\tsway_log(SWAY_DEBUG, \"Criteria already executed\");\n\t\t\tcontinue;\n\t\t}\n\t\tsway_log(SWAY_DEBUG, \"for_window '%s' matches view %p, cmd: '%s'\",\n\t\t\t\tcriteria->raw, view, criteria->cmdlist);\n\t\tlist_add(view->executed_criteria, criteria);\n\t\tlist_t *res_list = execute_command(criteria->cmdlist, NULL, view->container);\n\t\twhile (res_list->length) {\n\t\t\tstruct cmd_results *res = res_list->items[0];\n\t\t\tif (res->status != CMD_SUCCESS) {\n\t\t\t\tsway_log(SWAY_ERROR, \"for_window '%s' failed: %s\", criteria->raw, res->error);\n\t\t\t}\n\t\t\tfree_cmd_results(res);\n\t\t\tlist_del(res_list, 0);\n\t\t}\n\t\tlist_free(res_list);\n\t}\n\tlist_free(criterias);\n}\n\nstatic void view_populate_pid(struct sway_view *view) {\n\tpid_t pid;\n\tswitch (view->type) {\n#if WLR_HAS_XWAYLAND\n\tcase SWAY_VIEW_XWAYLAND:;\n\t\tstruct wlr_xwayland_surface *surf =\n\t\t\twlr_xwayland_surface_try_from_wlr_surface(view->surface);\n\t\tpid = surf->pid;\n\t\tbreak;\n#endif\n\tcase SWAY_VIEW_XDG_SHELL:;\n\t\tstruct wl_client *client =\n\t\t\twl_resource_get_client(view->surface->resource);\n\t\twl_client_get_credentials(client, &pid, NULL, NULL);\n\t\tbreak;\n\t}\n\tview->pid = pid;\n}\n\nvoid view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) {\n\tif (view->ctx) {\n\t\t// This ctx has been replaced\n\t\tlauncher_ctx_destroy(view->ctx);\n\t\tview->ctx = NULL;\n\t}\n\tif (ctx == NULL) {\n\t\treturn;\n\t}\n\tlauncher_ctx_consume(ctx);\n\n\tview->ctx = ctx;\n}\n\nstatic struct sway_workspace *select_workspace(struct sway_view *view) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\n\t// Check if there's any `assign` criteria for the view\n\tlist_t *criterias = criteria_for_view(view,\n\t\t\tCT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT);\n\tstruct sway_workspace *ws = NULL;\n\tfor (int i = 0; i < criterias->length; ++i) {\n\t\tstruct criteria *criteria = criterias->items[i];\n\t\tif (criteria->type == CT_ASSIGN_OUTPUT) {\n\t\t\tstruct sway_output *output = output_by_name_or_id(criteria->target);\n\t\t\tif (output) {\n\t\t\t\tws = output_get_active_workspace(output);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\t// CT_ASSIGN_WORKSPACE(_NUMBER)\n\t\t\tws = criteria->type == CT_ASSIGN_WORKSPACE_NUMBER ?\n\t\t\t\tworkspace_by_number(criteria->target) :\n\t\t\t\tworkspace_by_name(criteria->target);\n\n\t\t\tif (!ws) {\n\t\t\t\tif (strcasecmp(criteria->target, \"back_and_forth\") == 0) {\n\t\t\t\t\tif (seat->prev_workspace_name) {\n\t\t\t\t\t\tws = workspace_create(NULL, seat->prev_workspace_name);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tws = workspace_create(NULL, criteria->target);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tlist_free(criterias);\n\tif (ws) {\n\t\tview_assign_ctx(view, NULL);\n\t\treturn ws;\n\t}\n\n\t// Check if there's a PID mapping\n\tws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;\n\tif (ws) {\n\t\tview_assign_ctx(view, NULL);\n\t\treturn ws;\n\t}\n\n\t// Use the focused workspace\n\tstruct sway_node *node = seat_get_focus_inactive(seat, &root->node);\n\tif (node && node->type == N_WORKSPACE) {\n\t\treturn node->sway_workspace;\n\t} else if (node && node->type == N_CONTAINER) {\n\t\treturn node->sway_container->pending.workspace;\n\t}\n\n\t// When there's no outputs connected, the above should match a workspace on\n\t// the noop output.\n\tsway_assert(false, \"Expected to find a workspace\");\n\treturn NULL;\n}\n\nstatic void update_ext_foreign_toplevel(struct sway_view *view) {\n\tstruct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = {\n\t\t.app_id = view_get_app_id(view),\n\t\t.title = view_get_title(view),\n\t};\n\twlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state);\n}\n\nstatic bool should_focus(struct sway_view *view) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_container *prev_con = seat_get_focused_container(seat);\n\tstruct sway_workspace *prev_ws = seat_get_focused_workspace(seat);\n\tstruct sway_workspace *map_ws = view->container->pending.workspace;\n\n\tif (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\treturn true;\n\t}\n\n\t// View opened \"under\" fullscreen view should not be given focus.\n\tif (root->fullscreen_global || !map_ws || map_ws->fullscreen) {\n\t\treturn false;\n\t}\n\n\t// Views can only take focus if they are mapped into the active workspace\n\tif (prev_ws != map_ws) {\n\t\treturn false;\n\t}\n\n\t// If the view is the only one in the focused workspace, it'll get focus\n\t// regardless of any no_focus criteria.\n\tif (!view->container->pending.parent && !prev_con) {\n\t\tsize_t num_children = view->container->pending.workspace->tiling->length +\n\t\t\tview->container->pending.workspace->floating->length;\n\t\tif (num_children == 1) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Check no_focus criteria\n\tlist_t *criterias = criteria_for_view(view, CT_NO_FOCUS);\n\tsize_t len = criterias->length;\n\tlist_free(criterias);\n\treturn len == 0;\n}\n\nstatic void handle_foreign_activate_request(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_view *view = wl_container_of(\n\t\t\tlistener, view, foreign_activate_request);\n\tstruct wlr_foreign_toplevel_handle_v1_activated_event *event = data;\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tif (seat->wlr_seat == event->seat) {\n\t\t\tif (container_is_scratchpad_hidden_or_child(view->container)) {\n\t\t\t\troot_scratchpad_show(view->container);\n\t\t\t}\n\t\t\tseat_set_focus_container(seat, view->container);\n\t\t\tseat_consider_warp_to_focus(seat);\n\t\t\tcontainer_raise_floating(view->container);\n\t\t\tbreak;\n\t\t}\n\t}\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_foreign_fullscreen_request(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_view *view = wl_container_of(\n\t\t\tlistener, view, foreign_fullscreen_request);\n\tstruct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;\n\n\t// Match fullscreen command behavior for scratchpad hidden views\n\tstruct sway_container *container = view->container;\n\tif (!container->pending.workspace) {\n\t\twhile (container->pending.parent) {\n\t\t\tcontainer = container->pending.parent;\n\t\t}\n\t}\n\n\tif (event->fullscreen && event->output && event->output->data) {\n\t\tstruct sway_output *output = event->output->data;\n\t\tstruct sway_workspace *ws = output_get_active_workspace(output);\n\t\tif (ws && !container_is_scratchpad_hidden(view->container)) {\n\t\t\tif (container_is_floating(view->container)) {\n\t\t\t\tworkspace_add_floating(ws, view->container);\n\t\t\t} else {\n\t\t\t\tworkspace_add_tiling(ws, view->container);\n\t\t\t}\n\t\t}\n\t}\n\n\tcontainer_set_fullscreen(container,\n\t\tevent->fullscreen ? FULLSCREEN_WORKSPACE : FULLSCREEN_NONE);\n\tif (event->fullscreen) {\n\t\tarrange_root();\n\t} else {\n\t\tif (container->pending.parent) {\n\t\t\tarrange_container(container->pending.parent);\n\t\t} else if (container->pending.workspace) {\n\t\t\tarrange_workspace(container->pending.workspace);\n\t\t}\n\t}\n\ttransaction_commit_dirty();\n}\n\nstatic void handle_foreign_close_request(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_view *view = wl_container_of(\n\t\t\tlistener, view, foreign_close_request);\n\tview_close(view);\n}\n\nstatic void handle_foreign_destroy(\n\t\tstruct wl_listener *listener, void *data) {\n\tstruct sway_view *view = wl_container_of(\n\t\t\tlistener, view, foreign_destroy);\n\n\twl_list_remove(&view->foreign_activate_request.link);\n\twl_list_remove(&view->foreign_fullscreen_request.link);\n\twl_list_remove(&view->foreign_close_request.link);\n\twl_list_remove(&view->foreign_destroy.link);\n}\n\nvoid view_map(struct sway_view *view, struct wlr_surface *wlr_surface,\n\t\t\t  bool fullscreen, struct wlr_output *fullscreen_output,\n\t\t\t  bool decoration) {\n\tif (!sway_assert(view->surface == NULL, \"cannot map mapped view\")) {\n\t\treturn;\n\t}\n\tview->surface = wlr_surface;\n\tview_populate_pid(view);\n\tview->container = container_create(view);\n\n\tif (view->ctx == NULL) {\n\t\tstruct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid);\n\t\tif (ctx != NULL) {\n\t\t\tview_assign_ctx(view, ctx);\n\t\t}\n\t}\n\n\t// If there is a request to be opened fullscreen on a specific output, try\n\t// to honor that request. Otherwise, fallback to assigns, pid mappings,\n\t// focused workspace, etc\n\tstruct sway_workspace *ws = NULL;\n\tif (fullscreen_output && fullscreen_output->data) {\n\t\tstruct sway_output *output = fullscreen_output->data;\n\t\tws = output_get_active_workspace(output);\n\t}\n\tif (!ws) {\n\t\tws = select_workspace(view);\n\t}\n\n\tif (ws && ws->output) {\n\t\t// Once the output is determined, we can notify the client early about\n\t\t// scale to reduce startup jitter.\n\t\tfloat scale = ws->output->wlr_output->scale;\n\t\twlr_fractional_scale_v1_notify_scale(wlr_surface, scale);\n\t\twlr_surface_set_preferred_buffer_scale(wlr_surface, ceil(scale));\n\t}\n\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_node *node =\n\t\tseat_get_focus_inactive(seat, ws ? &ws->node : &root->node);\n\tstruct sway_container *target_sibling = NULL;\n\tif (node && node->type == N_CONTAINER) {\n\t\tif (container_is_floating(node->sway_container)) {\n\t\t\t// If we're about to launch the view into the floating container, then\n\t\t\t// launch it as a tiled view instead.\n\t\t\tif (ws) {\n\t\t\t\ttarget_sibling = seat_get_focus_inactive_tiling(seat, ws);\n\t\t\t\tif (target_sibling) {\n\t\t\t\t\tstruct sway_container *con =\n\t\t\t\t\t\tseat_get_focus_inactive_view(seat, &target_sibling->node);\n\t\t\t\t\tif (con)  {\n\t\t\t\t\t\ttarget_sibling = con;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tws = seat_get_last_known_workspace(seat);\n\t\t\t}\n\t\t} else {\n\t\t\ttarget_sibling = node->sway_container;\n\t\t}\n\t}\n\n\tstruct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {\n\t\t.app_id = view_get_app_id(view),\n\t\t.title = view_get_title(view),\n\t};\n\tview->ext_foreign_toplevel =\n\t\twlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state);\n\tview->ext_foreign_toplevel->data = view;\n\n\tview->foreign_toplevel =\n\t\twlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);\n\tview->foreign_activate_request.notify = handle_foreign_activate_request;\n\twl_signal_add(&view->foreign_toplevel->events.request_activate,\n\t\t\t&view->foreign_activate_request);\n\tview->foreign_fullscreen_request.notify = handle_foreign_fullscreen_request;\n\twl_signal_add(&view->foreign_toplevel->events.request_fullscreen,\n\t\t\t&view->foreign_fullscreen_request);\n\tview->foreign_close_request.notify = handle_foreign_close_request;\n\twl_signal_add(&view->foreign_toplevel->events.request_close,\n\t\t\t&view->foreign_close_request);\n\tview->foreign_destroy.notify = handle_foreign_destroy;\n\twl_signal_add(&view->foreign_toplevel->events.destroy,\n\t\t\t&view->foreign_destroy);\n\n\tstruct sway_container *container = view->container;\n\tif (target_sibling) {\n\t\tcontainer_add_sibling(target_sibling, container, 1);\n\t} else if (ws) {\n\t\tcontainer = workspace_add_tiling(ws, container);\n\t}\n\tipc_event_window(view->container, \"new\");\n\n\tif (decoration) {\n\t\tview_update_csd_from_client(view, decoration);\n\t}\n\n\tif (view->impl->wants_floating && view->impl->wants_floating(view)) {\n\t\tview->container->pending.border = config->floating_border;\n\t\tview->container->pending.border_thickness = config->floating_border_thickness;\n\t\tcontainer_set_floating(view->container, true);\n\t} else {\n\t\tview->container->pending.border = config->border;\n\t\tview->container->pending.border_thickness = config->border_thickness;\n\t\tview_set_tiled(view, true);\n\t}\n\n\tif (config->popup_during_fullscreen == POPUP_LEAVE &&\n\t\t\tcontainer->pending.workspace &&\n\t\t\tcontainer->pending.workspace->fullscreen &&\n\t\t\tcontainer->pending.workspace->fullscreen->view) {\n\t\tstruct sway_container *fs = container->pending.workspace->fullscreen;\n\t\tif (view_is_transient_for(view, fs->view)) {\n\t\t\tcontainer_set_fullscreen(fs, false);\n\t\t}\n\t}\n\n\tview_update_title(view, false);\n\tcontainer_update_representation(container);\n\n\tif (fullscreen) {\n\t\tcontainer_set_fullscreen(view->container, true);\n\t\tarrange_workspace(view->container->pending.workspace);\n\t} else {\n\t\tif (container->pending.parent) {\n\t\t\tarrange_container(container->pending.parent);\n\t\t} else if (container->pending.workspace) {\n\t\t\tarrange_workspace(container->pending.workspace);\n\t\t}\n\t}\n\n\tview_execute_criteria(view);\n\n\tbool set_focus = should_focus(view);\n\n#if WLR_HAS_XWAYLAND\n\tstruct wlr_xwayland_surface *xsurface;\n\tif ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {\n\t\tset_focus &= wlr_xwayland_surface_icccm_input_model(xsurface) !=\n\t\t\t\tWLR_ICCCM_INPUT_MODEL_NONE;\n\t}\n#endif\n\n\tif (set_focus) {\n\t\tinput_manager_set_focus(&view->container->node);\n\t}\n\n\tif (view->ext_foreign_toplevel) {\n\t\tupdate_ext_foreign_toplevel(view);\n\t}\n\n\tconst char *app_id;\n\tconst char *class;\n\tif ((app_id = view_get_app_id(view)) != NULL) {\n\t\twlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);\n\t} else if ((class = view_get_class(view)) != NULL) {\n\t\twlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class);\n\t}\n}\n\nvoid view_unmap(struct sway_view *view) {\n\twl_signal_emit_mutable(&view->events.unmap, view);\n\n\tview->executed_criteria->length = 0;\n\n\tif (view->urgent_timer) {\n\t\twl_event_source_remove(view->urgent_timer);\n\t\tview->urgent_timer = NULL;\n\t}\n\n\tif (view->ext_foreign_toplevel) {\n\t\twlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel);\n\t\tview->ext_foreign_toplevel = NULL;\n\t}\n\n\tif (view->foreign_toplevel) {\n\t\twlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);\n\t\tview->foreign_toplevel = NULL;\n\t}\n\n\tstruct sway_container *parent = view->container->pending.parent;\n\tstruct sway_workspace *ws = view->container->pending.workspace;\n\tcontainer_begin_destroy(view->container);\n\tif (parent) {\n\t\tcontainer_reap_empty(parent);\n\t} else if (ws) {\n\t\tworkspace_consider_destroy(ws);\n\t}\n\n\tif (root->fullscreen_global) {\n\t\t// Container may have been a child of the root fullscreen container\n\t\tarrange_root();\n\t} else if (ws && !ws->node.destroying) {\n\t\tarrange_workspace(ws);\n\t\tworkspace_detect_urgent(ws);\n\t}\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tseat->cursor->image_surface = NULL;\n\t\tif (seat->cursor->active_constraint) {\n\t\t\tstruct wlr_surface *constrain_surface =\n\t\t\t\tseat->cursor->active_constraint->surface;\n\t\t\tif (view_from_wlr_surface(constrain_surface) == view) {\n\t\t\t\tsway_cursor_constrain(seat->cursor, NULL);\n\t\t\t}\n\t\t}\n\t\tseat_consider_warp_to_focus(seat);\n\t}\n\n\ttransaction_commit_dirty();\n\tview->surface = NULL;\n}\n\nvoid view_update_size(struct sway_view *view) {\n\tstruct sway_container *con = view->container;\n\tcon->pending.content_width = view->geometry.width;\n\tcon->pending.content_height = view->geometry.height;\n\tcontainer_set_geometry_from_content(con);\n}\n\nvoid view_center_and_clip_surface(struct sway_view *view) {\n\tstruct sway_container *con = view->container;\n\n\tbool clip_to_geometry = true;\n\n\tif (container_is_floating(con)) {\n\t\t// We always center the current coordinates rather than the next, as the\n\t\t// geometry immediately affects the currently active rendering.\n\t\tint x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);\n\t\tint y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);\n\t\tclip_to_geometry = !view->using_csd;\n\n\t\twlr_scene_node_set_position(&view->content_tree->node, x, y);\n\t} else {\n\t\twlr_scene_node_set_position(&view->content_tree->node, 0, 0);\n\t}\n\n\t// only make sure to clip the content if there is content to clip\n\tif (!wl_list_empty(&con->view->content_tree->children)) {\n\t\tstruct wlr_box clip = {0};\n\t\tif (clip_to_geometry) {\n\t\t\tclip = (struct wlr_box){\n\t\t\t\t.x = con->view->geometry.x,\n\t\t\t\t.y = con->view->geometry.y,\n\t\t\t\t.width = con->current.content_width,\n\t\t\t\t.height = con->current.content_height,\n\t\t\t};\n\t\t}\n\t\twlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip);\n\t}\n}\n\nstruct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {\n\tstruct wlr_xdg_surface *xdg_surface;\n\tif ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) {\n\t\treturn view_from_wlr_xdg_surface(xdg_surface);\n\t}\n#if WLR_HAS_XWAYLAND\n\tstruct wlr_xwayland_surface *xsurface;\n\tif ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {\n\t\treturn view_from_wlr_xwayland_surface(xsurface);\n\t}\n#endif\n\tstruct wlr_subsurface *subsurface;\n\tif ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) {\n\t\treturn view_from_wlr_surface(subsurface->parent);\n\t}\n\tif (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) {\n\t\treturn NULL;\n\t}\n\tif (wlr_session_lock_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) {\n\t\treturn NULL;\n\t}\n\n\tconst char *role = wlr_surface->role ? wlr_surface->role->name : NULL;\n\tsway_log(SWAY_DEBUG, \"Surface of unknown type (role %s): %p\",\n\t\trole, wlr_surface);\n\treturn NULL;\n}\n\nvoid view_update_app_id(struct sway_view *view) {\n\tconst char *app_id = view_get_app_id(view);\n\n\tif (view->foreign_toplevel && app_id) {\n\t\twlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);\n\t}\n\n\tif (view->ext_foreign_toplevel) {\n\t\tupdate_ext_foreign_toplevel(view);\n\t}\n}\n\nvoid view_update_title(struct sway_view *view, bool force) {\n\tconst char *title = view_get_title(view);\n\n\tif (!force) {\n\t\tif (title && view->container->title &&\n\t\t\t\tstrcmp(title, view->container->title) == 0) {\n\t\t\treturn;\n\t\t}\n\t\tif (!title && !view->container->title) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfree(view->container->title);\n\tfree(view->container->formatted_title);\n\n\tsize_t len = parse_title_format(view->container, NULL);\n\n\tif (len) {\n\t\tchar *buffer = calloc(len + 1, sizeof(char));\n\t\tif (!sway_assert(buffer, \"Unable to allocate title string\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tparse_title_format(view->container, buffer);\n\t\tview->container->formatted_title = buffer;\n\t} else {\n\t\tview->container->formatted_title = NULL;\n\t}\n\n\tview->container->title = title ? strdup(title) : NULL;\n\n\t// Update title after the global font height is updated\n\tif (view->container->title_bar.title_text && len) {\n\t\tsway_text_node_set_text(view->container->title_bar.title_text,\n\t\t\tview->container->formatted_title);\n\t\tcontainer_arrange_title_bar(view->container);\n\t} else {\n\t\tcontainer_update_title_bar(view->container);\n\t}\n\n\tipc_event_window(view->container, \"title\");\n\n\tif (view->foreign_toplevel && title) {\n\t\twlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);\n\t}\n\n\tif (view->ext_foreign_toplevel) {\n\t\tupdate_ext_foreign_toplevel(view);\n\t}\n}\n\nbool view_is_visible(struct sway_view *view) {\n\tif (view->container->node.destroying) {\n\t\treturn false;\n\t}\n\tstruct sway_workspace *workspace = view->container->pending.workspace;\n\tif (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {\n\t\tbool fs_global_descendant = false;\n\t\tstruct sway_container *parent = view->container->pending.parent;\n\t\twhile (parent) {\n\t\t\tif (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {\n\t\t\t\tfs_global_descendant = true;\n\t\t\t}\n\t\t\tparent = parent->pending.parent;\n\t\t}\n\t\tif (!fs_global_descendant) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!container_is_sticky_or_child(view->container) && workspace &&\n\t\t\t!workspace_is_visible(workspace)) {\n\t\treturn false;\n\t}\n\t// Check view isn't in a tabbed or stacked container on an inactive tab\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_container *con = view->container;\n\twhile (con) {\n\t\tenum sway_container_layout layout = container_parent_layout(con);\n\t\tif ((layout == L_TABBED || layout == L_STACKED)\n\t\t\t\t&& !container_is_floating(con)) {\n\t\t\tstruct sway_node *parent = con->pending.parent ?\n\t\t\t\t&con->pending.parent->node : &con->pending.workspace->node;\n\t\t\tif (seat_get_active_tiling_child(seat, parent) != &con->node) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tcon = con->pending.parent;\n\t}\n\t// Check view isn't hidden by another fullscreen view\n\tstruct sway_container *fs = root->fullscreen_global ?\n\t\troot->fullscreen_global : workspace->fullscreen;\n\tif (fs && !container_is_fullscreen_or_child(view->container) &&\n\t\t\t!container_is_transient_for(view->container, fs)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nvoid view_set_urgent(struct sway_view *view, bool enable) {\n\tif (view_is_urgent(view) == enable) {\n\t\treturn;\n\t}\n\tif (enable) {\n\t\tstruct sway_seat *seat = input_manager_current_seat();\n\t\tif (seat_get_focused_container(seat) == view->container) {\n\t\t\treturn;\n\t\t}\n\t\tclock_gettime(CLOCK_MONOTONIC, &view->urgent);\n\t\tcontainer_update_itself_and_parents(view->container);\n\t} else {\n\t\tview->urgent = (struct timespec){ 0 };\n\t\tif (view->urgent_timer) {\n\t\t\twl_event_source_remove(view->urgent_timer);\n\t\t\tview->urgent_timer = NULL;\n\t\t}\n\t}\n\n\tipc_event_window(view->container, \"urgent\");\n\n\tif (!container_is_scratchpad_hidden_or_child(view->container)) {\n\t\tworkspace_detect_urgent(view->container->pending.workspace);\n\t}\n}\n\nbool view_is_urgent(struct sway_view *view) {\n\treturn view->urgent.tv_sec || view->urgent.tv_nsec;\n}\n\nvoid view_remove_saved_buffer(struct sway_view *view) {\n\tif (!sway_assert(view->saved_surface_tree, \"Expected a saved buffer\")) {\n\t\treturn;\n\t}\n\n\twlr_scene_node_destroy(&view->saved_surface_tree->node);\n\tview->saved_surface_tree = NULL;\n\twlr_scene_node_set_enabled(&view->content_tree->node, true);\n}\n\nstatic void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,\n\t\tint sx, int sy, void *data) {\n\tstruct wlr_scene_tree *tree = data;\n\n\tstruct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL);\n\tif (!sbuf) {\n\t\tsway_log(SWAY_ERROR, \"Could not allocate a scene buffer when saving a surface\");\n\t\treturn;\n\t}\n\n\twlr_scene_buffer_set_dest_size(sbuf,\n\t\tbuffer->dst_width, buffer->dst_height);\n\twlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region);\n\twlr_scene_buffer_set_opacity(sbuf, buffer->opacity);\n\twlr_scene_buffer_set_filter_mode(sbuf, buffer->filter_mode);\n\twlr_scene_buffer_set_transfer_function(sbuf, buffer->transfer_function);\n\twlr_scene_buffer_set_primaries(sbuf, buffer->primaries);\n\twlr_scene_buffer_set_source_box(sbuf, &buffer->src_box);\n\twlr_scene_node_set_position(&sbuf->node, sx, sy);\n\twlr_scene_buffer_set_transform(sbuf, buffer->transform);\n\twlr_scene_buffer_set_buffer(sbuf, buffer->buffer);\n}\n\nvoid view_save_buffer(struct sway_view *view) {\n\tif (!sway_assert(!view->saved_surface_tree, \"Didn't expect saved buffer\")) {\n\t\tview_remove_saved_buffer(view);\n\t}\n\n\tview->saved_surface_tree = wlr_scene_tree_create(view->scene_tree);\n\tif (!view->saved_surface_tree) {\n\t\tsway_log(SWAY_ERROR, \"Could not allocate a scene tree node when saving a surface\");\n\t\treturn;\n\t}\n\n\t// Enable and disable the saved surface tree like so to atomitaclly update\n\t// the tree. This will prevent over damaging or other weirdness.\n\twlr_scene_node_set_enabled(&view->saved_surface_tree->node, false);\n\n\twlr_scene_node_for_each_buffer(&view->content_tree->node,\n\t\tview_save_buffer_iterator, view->saved_surface_tree);\n\n\twlr_scene_node_set_enabled(&view->content_tree->node, false);\n\twlr_scene_node_set_enabled(&view->saved_surface_tree->node, true);\n}\n\nbool view_is_transient_for(struct sway_view *child,\n\t\tstruct sway_view *ancestor) {\n\treturn child->impl->is_transient_for &&\n\t\tchild->impl->is_transient_for(child, ancestor);\n}\n\nbool view_can_tear(struct sway_view *view) {\n\tswitch (view->tearing_mode) {\n\tcase TEARING_OVERRIDE_FALSE:\n\t\treturn false;\n\tcase TEARING_OVERRIDE_TRUE:\n\t\treturn true;\n\tcase TEARING_WINDOW_HINT:\n\t\treturn view->tearing_hint ==\n\t\t\tWP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;\n\t}\n\treturn false;\n}\n\nstatic void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,\n\t\tint x, int y, void *data) {\n\tstruct timespec *when = data;\n\tstruct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer);\n\tif (scene_surface == NULL) {\n\t\treturn;\n\t}\n\twlr_surface_send_frame_done(scene_surface->surface, when);\n}\n\nvoid view_send_frame_done(struct sway_view *view) {\n\tstruct timespec when;\n\tclock_gettime(CLOCK_MONOTONIC, &when);\n\n\tstruct wlr_scene_node *node;\n\twl_list_for_each(node, &view->content_tree->children, link) {\n\t\twlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when);\n\t}\n}\n"
  },
  {
    "path": "sway/tree/workspace.c",
    "content": "#include <ctype.h>\n#include <limits.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <strings.h>\n#include \"stringop.h\"\n#include \"sway/input/input-manager.h\"\n#include \"sway/input/cursor.h\"\n#include \"sway/input/seat.h\"\n#include \"sway/ipc-server.h\"\n#include \"sway/output.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/container.h\"\n#include \"sway/tree/node.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nstruct workspace_config *workspace_find_config(const char *ws_name) {\n\tfor (int i = 0; i < config->workspace_configs->length; ++i) {\n\t\tstruct workspace_config *wsc = config->workspace_configs->items[i];\n\t\tif (strcmp(wsc->workspace, ws_name) == 0) {\n\t\t\treturn wsc;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct sway_output *workspace_get_initial_output(const char *name) {\n\t// Check workspace configs for a workspace<->output pair\n\tstruct workspace_config *wsc = workspace_find_config(name);\n\tif (wsc) {\n\t\tfor (int i = 0; i < wsc->outputs->length; i++) {\n\t\t\tstruct sway_output *output =\n\t\t\t\toutput_by_name_or_id(wsc->outputs->items[i]);\n\t\t\tif (output) {\n\t\t\t\treturn output;\n\t\t\t}\n\t\t}\n\t}\n\t// Otherwise try to put it on the focused output\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_node *focus = seat_get_focus_inactive(seat, &root->node);\n\tif (focus && focus->type == N_WORKSPACE) {\n\t\treturn focus->sway_workspace->output;\n\t} else if (focus && focus->type == N_CONTAINER) {\n\t\treturn focus->sway_container->pending.workspace->output;\n\t}\n\t// Fallback to the first output or the headless output\n\treturn root->outputs->length ? root->outputs->items[0] : root->fallback_output;\n}\n\nstruct sway_workspace *workspace_create(struct sway_output *output,\n\t\tconst char *name) {\n\tsway_assert(name, \"NULL name given to workspace_create\");\n\n\tif (output == NULL) {\n\t\toutput = workspace_get_initial_output(name);\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Adding workspace %s for output %s\", name,\n\t\t\toutput->wlr_output->name);\n\n\tstruct sway_workspace *ws = calloc(1, sizeof(struct sway_workspace));\n\tif (!ws) {\n\t\tsway_log(SWAY_ERROR, \"Unable to allocate sway_workspace\");\n\t\treturn NULL;\n\t}\n\tnode_init(&ws->node, N_WORKSPACE, ws);\n\n\tbool failed = false;\n\tws->layers.tiling = alloc_scene_tree(root->staging, &failed);\n\tws->layers.fullscreen = alloc_scene_tree(root->staging, &failed);\n\n\tif (failed) {\n\t\twlr_scene_node_destroy(&ws->layers.tiling->node);\n\t\twlr_scene_node_destroy(&ws->layers.fullscreen->node);\n\t\tfree(ws);\n\t\treturn NULL;\n\t}\n\n\tws->name = strdup(name);\n\tws->prev_split_layout = L_NONE;\n\tws->layout = output_get_default_layout(output);\n\tws->floating = create_list();\n\tws->tiling = create_list();\n\tws->output_priority = create_list();\n\n\tws->gaps_outer = config->gaps_outer;\n\tws->gaps_inner = config->gaps_inner;\n\tif (name) {\n\t\tstruct workspace_config *wsc = workspace_find_config(name);\n\t\tif (wsc) {\n\t\t\tif (wsc->gaps_outer.top != INT_MIN) {\n\t\t\t\tws->gaps_outer.top = wsc->gaps_outer.top;\n\t\t\t}\n\t\t\tif (wsc->gaps_outer.right != INT_MIN) {\n\t\t\t\tws->gaps_outer.right = wsc->gaps_outer.right;\n\t\t\t}\n\t\t\tif (wsc->gaps_outer.bottom != INT_MIN) {\n\t\t\t\tws->gaps_outer.bottom = wsc->gaps_outer.bottom;\n\t\t\t}\n\t\t\tif (wsc->gaps_outer.left != INT_MIN) {\n\t\t\t\tws->gaps_outer.left = wsc->gaps_outer.left;\n\t\t\t}\n\t\t\tif (wsc->gaps_inner != INT_MIN) {\n\t\t\t\tws->gaps_inner = wsc->gaps_inner;\n\t\t\t}\n\n\t\t\t// Add output priorities\n\t\t\tfor (int i = 0; i < wsc->outputs->length; ++i) {\n\t\t\t\tchar *name = wsc->outputs->items[i];\n\t\t\t\tif (strcmp(name, \"*\") != 0) {\n\t\t\t\t\tlist_add(ws->output_priority, strdup(name));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// If not already added, add the output to the lowest priority\n\tworkspace_output_add_priority(ws, output);\n\n\toutput_add_workspace(output, ws);\n\toutput_sort_workspaces(output);\n\n\tipc_event_workspace(NULL, ws, \"init\");\n\twl_signal_emit_mutable(&root->events.new_node, &ws->node);\n\n\treturn ws;\n}\n\nvoid workspace_destroy(struct sway_workspace *workspace) {\n\tif (!sway_assert(workspace->node.destroying,\n\t\t\t\t\"Tried to free workspace which wasn't marked as destroying\")) {\n\t\treturn;\n\t}\n\tif (!sway_assert(workspace->node.ntxnrefs == 0, \"Tried to free workspace \"\n\t\t\t\t\"which is still referenced by transactions\")) {\n\t\treturn;\n\t}\n\n\tscene_node_disown_children(workspace->layers.tiling);\n\tscene_node_disown_children(workspace->layers.fullscreen);\n\twlr_scene_node_destroy(&workspace->layers.tiling->node);\n\twlr_scene_node_destroy(&workspace->layers.fullscreen->node);\n\n\tfree(workspace->name);\n\tfree(workspace->representation);\n\tlist_free_items_and_destroy(workspace->output_priority);\n\tlist_free(workspace->floating);\n\tlist_free(workspace->tiling);\n\tlist_free(workspace->current.floating);\n\tlist_free(workspace->current.tiling);\n\tfree(workspace);\n}\n\nvoid workspace_begin_destroy(struct sway_workspace *workspace) {\n\tsway_log(SWAY_DEBUG, \"Destroying workspace '%s'\", workspace->name);\n\tipc_event_workspace(NULL, workspace, \"empty\"); // intentional\n\twl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node);\n\n\tif (workspace->output) {\n\t\tworkspace_detach(workspace);\n\t}\n\tnode_set_dirty(&workspace->node);\n\tworkspace->node.destroying = true;\n}\n\nvoid workspace_consider_destroy(struct sway_workspace *ws) {\n\tif (ws->tiling->length || ws->floating->length) {\n\t\treturn;\n\t}\n\n\tif (ws->output && output_get_active_workspace(ws->output) == ws) {\n\t\treturn;\n\t}\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tstruct sway_node *node = seat_get_focus_inactive(seat, &root->node);\n\t\tif (node == &ws->node) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tworkspace_begin_destroy(ws);\n}\n\nstatic bool workspace_valid_on_output(const char *output_name,\n\t\tconst char *ws_name) {\n\tstruct workspace_config *wsc = workspace_find_config(ws_name);\n\tstruct sway_output *output = output_by_name_or_id(output_name);\n\tif (!output) {\n\t\treturn false;\n\t}\n\tif (!wsc) {\n\t\treturn true;\n\t}\n\n\tfor (int i = 0; i < wsc->outputs->length; i++) {\n\t\tstruct sway_output *ws_output =\n\t\t\toutput_by_name_or_id(wsc->outputs->items[i]);\n\t\tif (ws_output) {\n\t\t\treturn ws_output == output;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic void workspace_name_from_binding(const struct sway_binding * binding,\n\t\tconst char* output_name, int *min_order, char **earliest_name) {\n\tchar *cmdlist = strdup(binding->command);\n\tchar *dup = cmdlist;\n\tchar *name = NULL;\n\n\t// workspace n\n\tchar *cmd = argsep(&cmdlist, \" \", NULL);\n\tif (cmdlist) {\n\t\tname = argsep(&cmdlist, \",;\", NULL);\n\t}\n\n\t// TODO: support \"move container to workspace\" bindings as well\n\n\tif (strcmp(\"workspace\", cmd) == 0 && name) {\n\t\tchar *_target = strdup(name);\n\t\t_target = do_var_replacement(_target);\n\t\tstrip_quotes(_target);\n\t\tsway_log(SWAY_DEBUG, \"Got valid workspace command for target: '%s'\",\n\t\t\t\t_target);\n\n\t\t// Make sure that the command references an actual workspace\n\t\t// not a command about workspaces\n\t\tif (strcmp(_target, \"next\") == 0 ||\n\t\t\t\tstrcmp(_target, \"prev\") == 0 ||\n\t\t\t\tstrcmp(_target, \"next_on_output\") == 0 ||\n\t\t\t\tstrcmp(_target, \"prev_on_output\") == 0 ||\n\t\t\t\tstrcmp(_target, \"number\") == 0 ||\n\t\t\t\tstrcmp(_target, \"back_and_forth\") == 0 ||\n\t\t\t\tstrcmp(_target, \"current\") == 0) {\n\t\t\tfree(_target);\n\t\t\tfree(dup);\n\t\t\treturn;\n\t\t}\n\n\t\t// If the command is workspace number <name>, isolate the name\n\t\tif (has_prefix(_target, \"number \")) {\n\t\t\tsize_t length = strlen(_target) - strlen(\"number \") + 1;\n\t\t\tchar *temp = malloc(length);\n\t\t\tstrncpy(temp, _target + strlen(\"number \"), length - 1);\n\t\t\ttemp[length - 1] = '\\0';\n\t\t\tfree(_target);\n\t\t\t_target = temp;\n\t\t\tsway_log(SWAY_DEBUG, \"Isolated name from workspace number: '%s'\", _target);\n\n\t\t\t// Make sure the workspace number doesn't already exist\n\t\t\tif (isdigit(_target[0]) && workspace_by_number(_target)) {\n\t\t\t\tfree(_target);\n\t\t\t\tfree(dup);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Make sure that the workspace doesn't already exist\n\t\tif (workspace_by_name(_target)) {\n\t\t\tfree(_target);\n\t\t\tfree(dup);\n\t\t\treturn;\n\t\t}\n\n\t\t// make sure that the workspace can appear on the given\n\t\t// output\n\t\tif (!workspace_valid_on_output(output_name, _target)) {\n\t\t\tfree(_target);\n\t\t\tfree(dup);\n\t\t\treturn;\n\t\t}\n\n\t\tif (binding->order < *min_order) {\n\t\t\t*min_order = binding->order;\n\t\t\tfree(*earliest_name);\n\t\t\t*earliest_name = _target;\n\t\t\tsway_log(SWAY_DEBUG, \"Workspace: Found free name %s\", _target);\n\t\t} else {\n\t\t\tfree(_target);\n\t\t}\n\t}\n\tfree(dup);\n}\n\nchar *workspace_next_name(const char *output_name) {\n\tsway_log(SWAY_DEBUG, \"Workspace: Generating new workspace name for output %s\",\n\t\t\toutput_name);\n\t// Scan for available workspace names by looking through output-workspace\n\t// assignments primarily, falling back to bindings and numbers.\n\tstruct sway_mode *mode = config->current_mode;\n\n\tstruct sway_output *output = output_by_name_or_id(output_name);\n\tif (!output) {\n\t\treturn NULL;\n\t}\n\n\tint order = INT_MAX;\n\tchar *target = NULL;\n\tfor (int i = 0; i < mode->keysym_bindings->length; ++i) {\n\t\tworkspace_name_from_binding(mode->keysym_bindings->items[i],\n\t\t\t\toutput_name, &order, &target);\n\t}\n\tfor (int i = 0; i < mode->keycode_bindings->length; ++i) {\n\t\tworkspace_name_from_binding(mode->keycode_bindings->items[i],\n\t\t\t\toutput_name, &order, &target);\n\t}\n\tfor (int i = 0; i < config->workspace_configs->length; ++i) {\n\t\t// Unlike with bindings, this does not guarantee order\n\t\tconst struct workspace_config *wsc = config->workspace_configs->items[i];\n\t\tif (workspace_by_name(wsc->workspace)) {\n\t\t\tcontinue;\n\t\t}\n\t\tbool found = false;\n\t\tfor (int j = 0; j < wsc->outputs->length; ++j) {\n\t\t\tstruct sway_output *ws_output =\n\t\t\t\toutput_by_name_or_id(wsc->outputs->items[j]);\n\t\t\tif (ws_output) {\n\t\t\t\tif (ws_output == output) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tfree(target);\n\t\t\t\t\ttarget = strdup(wsc->workspace);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (found) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (target != NULL) {\n\t\treturn target;\n\t}\n\t// As a fall back, use the next available number\n\tchar name[12] = \"\";\n\tunsigned int ws_num = 1;\n\tdo {\n\t\tsnprintf(name, sizeof(name), \"%u\", ws_num++);\n\t} while (workspace_by_number(name));\n\treturn strdup(name);\n}\n\nstatic bool _workspace_by_number(struct sway_workspace *ws, void *data) {\n\tchar *name = data;\n\tchar *ws_name = ws->name;\n\twhile (isdigit(*name)) {\n\t\tif (*name++ != *ws_name++) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn !isdigit(*ws_name);\n}\n\nstruct sway_workspace *workspace_by_number(const char* name) {\n\treturn root_find_workspace(_workspace_by_number, (void *) name);\n}\n\nstatic bool _workspace_by_name(struct sway_workspace *ws, void *data) {\n\treturn strcasecmp(ws->name, data) == 0;\n}\n\nstruct sway_workspace *workspace_by_name(const char *name) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_workspace *current = seat_get_focused_workspace(seat);\n\n\tif (current && strcmp(name, \"prev\") == 0) {\n\t\treturn workspace_prev(current);\n\t} else if (current && strcmp(name, \"prev_on_output\") == 0) {\n\t\treturn workspace_output_prev(current);\n\t} else if (current && strcmp(name, \"next\") == 0) {\n\t\treturn workspace_next(current);\n\t} else if (current && strcmp(name, \"next_on_output\") == 0) {\n\t\treturn workspace_output_next(current);\n\t} else if (strcmp(name, \"current\") == 0) {\n\t\treturn current;\n\t} else if (strcasecmp(name, \"back_and_forth\") == 0) {\n\t\tstruct sway_seat *seat = input_manager_current_seat();\n\t\tif (!seat->prev_workspace_name) {\n\t\t\treturn NULL;\n\t\t}\n\t\treturn root_find_workspace(_workspace_by_name,\n\t\t\t\t(void*)seat->prev_workspace_name);\n\t} else {\n\t\treturn root_find_workspace(_workspace_by_name, (void*)name);\n\t}\n}\n\nstatic int workspace_get_number(struct sway_workspace *workspace) {\n\tchar *endptr = NULL;\n\terrno = 0;\n\tlong long n = strtoll(workspace->name, &endptr, 10);\n\tif (errno != 0 || n > INT32_MAX || n < 0 || endptr == workspace->name) {\n\t\tn = -1;\n\t}\n\treturn n;\n}\n\nstruct sway_workspace *workspace_prev(struct sway_workspace *workspace) {\n\tint n = workspace_get_number(workspace);\n\tstruct sway_workspace *prev = NULL, *last = NULL, *other = NULL;\n\tbool found = false;\n\tif (n < 0) {\n\t\t// Find the prev named workspace\n\t\tint othern = -1;\n\t\tfor (int i = root->outputs->length - 1; i >= 0; i--) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tfor (int j = output->workspaces->length - 1; j >= 0; j--) {\n\t\t\t\tstruct sway_workspace *ws = output->workspaces->items[j];\n\t\t\t\tint wsn = workspace_get_number(ws);\n\t\t\t\tif (!last) {\n\t\t\t\t\t// The first workspace in reverse order\n\t\t\t\t\tlast = ws;\n\t\t\t\t}\n\t\t\t\tif (!other || (wsn >= 0 && wsn > othern)) {\n\t\t\t\t\t// The last (greatest) numbered workspace.\n\t\t\t\t\tother = ws;\n\t\t\t\t\tothern = workspace_get_number(other);\n\t\t\t\t}\n\t\t\t\tif (ws == workspace) {\n\t\t\t\t\tfound = true;\n\t\t\t\t} else if (wsn < 0 && found) {\n\t\t\t\t\t// Found a non-numbered workspace before current\n\t\t\t\t\treturn ws;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Find the prev numbered workspace\n\t\tint prevn = -1, lastn = -1;\n\t\tfor (int i = root->outputs->length - 1; i >= 0; i--) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tfor (int j = output->workspaces->length - 1; j >= 0; j--) {\n\t\t\t\tstruct sway_workspace *ws = output->workspaces->items[j];\n\t\t\t\tint wsn = workspace_get_number(ws);\n\t\t\t\tif (!last || (wsn >= 0 && wsn > lastn)) {\n\t\t\t\t\t// The greatest numbered (or last) workspace\n\t\t\t\t\tlast = ws;\n\t\t\t\t\tlastn = workspace_get_number(last);\n\t\t\t\t}\n\t\t\t\tif (!other && wsn < 0) {\n\t\t\t\t\t// The last named workspace\n\t\t\t\t\tother = ws;\n\t\t\t\t}\n\t\t\t\tif (wsn < 0) {\n\t\t\t\t\t// Haven't reached the numbered workspaces\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (wsn < n && (!prev || wsn > prevn)) {\n\t\t\t\t\t// The closest workspace before the current\n\t\t\t\t\tprev = ws;\n\t\t\t\t\tprevn = workspace_get_number(prev);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!prev) {\n\t\tprev = other ? other : last;\n\t}\n\treturn prev;\n}\n\nstruct sway_workspace *workspace_next(struct sway_workspace *workspace) {\n\tint n = workspace_get_number(workspace);\n\tstruct sway_workspace *next = NULL, *first = NULL, *other = NULL;\n\tbool found = false;\n\tif (n < 0) {\n\t\t// Find the next named workspace\n\t\tint othern = -1;\n\t\tfor (int i = 0; i < root->outputs->length; i++) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tfor (int j = 0; j < output->workspaces->length; j++) {\n\t\t\t\tstruct sway_workspace *ws = output->workspaces->items[j];\n\t\t\t\tint wsn = workspace_get_number(ws);\n\t\t\t\tif (!first) {\n\t\t\t\t\t// The first named workspace\n\t\t\t\t\tfirst = ws;\n\t\t\t\t}\n\t\t\t\tif (!other || (wsn >= 0 && wsn < othern)) {\n\t\t\t\t\t// The first (least) numbered workspace\n\t\t\t\t\tother = ws;\n\t\t\t\t\tothern = workspace_get_number(other);\n\t\t\t\t}\n\t\t\t\tif (ws == workspace) {\n\t\t\t\t\tfound = true;\n\t\t\t\t} else if (wsn < 0 && found) {\n\t\t\t\t\t// The first non-numbered workspace after the current\n\t\t\t\t\treturn ws;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Find the next numbered workspace\n\t\tint nextn = -1, firstn = -1;\n\t\tfor (int i = 0; i < root->outputs->length; i++) {\n\t\t\tstruct sway_output *output = root->outputs->items[i];\n\t\t\tfor (int j = 0; j < output->workspaces->length; j++) {\n\t\t\t\tstruct sway_workspace *ws = output->workspaces->items[j];\n\t\t\t\tint wsn = workspace_get_number(ws);\n\t\t\t\tif (!first || (wsn >= 0 && wsn < firstn)) {\n\t\t\t\t\t// The first (or least numbered) workspace\n\t\t\t\t\tfirst = ws;\n\t\t\t\t\tfirstn = workspace_get_number(first);\n\t\t\t\t}\n\t\t\t\tif (!other && wsn < 0) {\n\t\t\t\t\t// The first non-numbered workspace\n\t\t\t\t\tother = ws;\n\t\t\t\t}\n\t\t\t\tif (wsn < 0) {\n\t\t\t\t\t// Checked all the numbered workspaces\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (n < wsn && (!next || wsn < nextn)) {\n\t\t\t\t\t// The first workspace numerically after the current\n\t\t\t\t\tnext = ws;\n\t\t\t\t\tnextn = workspace_get_number(next);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!next) {\n\t\t// If there is no next workspace from the same category, return the\n\t\t// first from this category.\n\t\tnext = other ? other : first;\n\t}\n\treturn next;\n}\n\n/**\n * Get the previous or next workspace on the specified output. Wraps around at\n * the end and beginning.  If next is false, the previous workspace is returned,\n * otherwise the next one is returned.\n */\nstatic struct sway_workspace *workspace_output_prev_next_impl(\n\t\tstruct sway_output *output, int dir) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_workspace *workspace = seat_get_focused_workspace(seat);\n\tif (!workspace) {\n\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\"No focused workspace to base prev/next on output off of\");\n\t\treturn NULL;\n\t}\n\n\tint index = list_find(output->workspaces, workspace);\n\tsize_t new_index = wrap(index + dir, output->workspaces->length);\n\treturn output->workspaces->items[new_index];\n}\n\n\nstruct sway_workspace *workspace_output_next(struct sway_workspace *current) {\n\treturn workspace_output_prev_next_impl(current->output, 1);\n}\n\nstruct sway_workspace *workspace_output_prev(struct sway_workspace *current) {\n\treturn workspace_output_prev_next_impl(current->output, -1);\n}\n\nstruct sway_workspace *workspace_auto_back_and_forth(\n\t\tstruct sway_workspace *workspace) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\tstruct sway_workspace *active_ws = NULL;\n\tstruct sway_node *focus = seat_get_focus_inactive(seat, &root->node);\n\tif (focus && focus->type == N_WORKSPACE) {\n\t\tactive_ws = focus->sway_workspace;\n\t} else if (focus && focus->type == N_CONTAINER) {\n\t\tactive_ws = focus->sway_container->pending.workspace;\n\t}\n\n\tif (config->auto_back_and_forth && active_ws && active_ws == workspace &&\n\t\t\tseat->prev_workspace_name) {\n\t\tstruct sway_workspace *new_ws =\n\t\t\tworkspace_by_name(seat->prev_workspace_name);\n\t\tworkspace = new_ws ?\n\t\t\tnew_ws :\n\t\t\tworkspace_create(NULL, seat->prev_workspace_name);\n\t}\n\treturn workspace;\n}\n\nbool workspace_switch(struct sway_workspace *workspace) {\n\tstruct sway_seat *seat = input_manager_current_seat();\n\n\tsway_log(SWAY_DEBUG, \"Switching to workspace %p:%s\",\n\t\tworkspace, workspace->name);\n\tstruct sway_node *next = seat_get_focus_inactive(seat, &workspace->node);\n\tif (next == NULL) {\n\t\tnext = &workspace->node;\n\t}\n\tseat_set_focus(seat, next);\n\tarrange_workspace(workspace);\n\treturn true;\n}\n\nbool workspace_is_visible(struct sway_workspace *ws) {\n\tif (ws->node.destroying) {\n\t\treturn false;\n\t}\n\treturn output_get_active_workspace(ws->output) == ws;\n}\n\nbool workspace_is_empty(struct sway_workspace *ws) {\n\tif (ws->tiling->length) {\n\t\treturn false;\n\t}\n\t// Sticky views are not considered to be part of this workspace\n\tfor (int i = 0; i < ws->floating->length; ++i) {\n\t\tstruct sway_container *floater = ws->floating->items[i];\n\t\tif (!container_is_sticky(floater)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic int find_output(const void *id1, const void *id2) {\n\treturn strcmp(id1, id2);\n}\n\nstatic int workspace_output_get_priority(struct sway_workspace *ws,\n\t\tstruct sway_output *output) {\n\tchar identifier[128];\n\toutput_get_identifier(identifier, sizeof(identifier), output);\n\tint index_id = list_seq_find(ws->output_priority, find_output, identifier);\n\tint index_name = list_seq_find(ws->output_priority, find_output,\n\t\t\toutput->wlr_output->name);\n\treturn index_name < 0 || index_id < index_name ? index_id : index_name;\n}\n\nvoid workspace_output_raise_priority(struct sway_workspace *ws,\n\t\tstruct sway_output *old_output, struct sway_output *output) {\n\tint old_index = workspace_output_get_priority(ws, old_output);\n\tif (old_index < 0) {\n\t\treturn;\n\t}\n\n\tint new_index = workspace_output_get_priority(ws, output);\n\tif (new_index < 0) {\n\t\tchar identifier[128];\n\t\toutput_get_identifier(identifier, sizeof(identifier), output);\n\t\tlist_insert(ws->output_priority, old_index, strdup(identifier));\n\t} else if (new_index > old_index) {\n\t\tchar *name = ws->output_priority->items[new_index];\n\t\tlist_del(ws->output_priority, new_index);\n\t\tlist_insert(ws->output_priority, old_index, name);\n\t}\n}\n\nvoid workspace_output_add_priority(struct sway_workspace *workspace,\n\t\tstruct sway_output *output) {\n\tif (workspace_output_get_priority(workspace, output) < 0) {\n\t\tchar identifier[128];\n\t\toutput_get_identifier(identifier, sizeof(identifier), output);\n\t\tlist_add(workspace->output_priority, strdup(identifier));\n\t}\n}\n\nstruct sway_output *workspace_output_get_highest_available(\n\t\tstruct sway_workspace *ws) {\n\tfor (int i = 0; i < ws->output_priority->length; i++) {\n\t\tconst char *name = ws->output_priority->items[i];\n\t\tstruct sway_output *output = output_by_name_or_id(name);\n\t\tif (output) {\n\t\t\treturn output;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nstatic bool find_urgent_iterator(struct sway_container *con, void *data) {\n\treturn con->view && view_is_urgent(con->view);\n}\n\nvoid workspace_detect_urgent(struct sway_workspace *workspace) {\n\tbool new_urgent = (bool)workspace_find_container(workspace,\n\t\t\tfind_urgent_iterator, NULL);\n\n\tif (workspace->urgent != new_urgent) {\n\t\tworkspace->urgent = new_urgent;\n\t\tipc_event_workspace(NULL, workspace, \"urgent\");\n\t}\n}\n\nvoid workspace_for_each_container(struct sway_workspace *ws,\n\t\tvoid (*f)(struct sway_container *con, void *data), void *data) {\n\t// Tiling\n\tfor (int i = 0; i < ws->tiling->length; ++i) {\n\t\tstruct sway_container *container = ws->tiling->items[i];\n\t\tf(container, data);\n\t\tcontainer_for_each_child(container, f, data);\n\t}\n\t// Floating\n\tfor (int i = 0; i < ws->floating->length; ++i) {\n\t\tstruct sway_container *container = ws->floating->items[i];\n\t\tf(container, data);\n\t\tcontainer_for_each_child(container, f, data);\n\t}\n}\n\nstruct sway_container *workspace_find_container(struct sway_workspace *ws,\n\t\tbool (*test)(struct sway_container *con, void *data), void *data) {\n\tstruct sway_container *result = NULL;\n    if (ws == NULL){\n        sway_log(SWAY_ERROR, \"Cannot find container with no workspace.\");\n        return NULL;\n    }\n\n\t// Tiling\n\tfor (int i = 0; i < ws->tiling->length; ++i) {\n\t\tstruct sway_container *child = ws->tiling->items[i];\n\t\tif (test(child, data)) {\n\t\t\treturn child;\n\t\t}\n\t\tif ((result = container_find_child(child, test, data))) {\n\t\t\treturn result;\n\t\t}\n\t}\n\t// Floating\n\tfor (int i = 0; i < ws->floating->length; ++i) {\n\t\tstruct sway_container *child = ws->floating->items[i];\n\t\tif (test(child, data)) {\n\t\t\treturn child;\n\t\t}\n\t\tif ((result = container_find_child(child, test, data))) {\n\t\t\treturn result;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void set_workspace(struct sway_container *container, void *data) {\n\tcontainer->pending.workspace = container->pending.parent->pending.workspace;\n}\n\nstatic void workspace_attach_tiling(struct sway_workspace *ws,\n\t\tstruct sway_container *con) {\n\tlist_add(ws->tiling, con);\n\tcon->pending.workspace = ws;\n\tcontainer_for_each_child(con, set_workspace, NULL);\n\tcontainer_handle_fullscreen_reparent(con);\n\tworkspace_update_representation(ws);\n\tnode_set_dirty(&ws->node);\n\tnode_set_dirty(&con->node);\n}\n\nstruct sway_container *workspace_wrap_children(struct sway_workspace *ws) {\n\tstruct sway_container *fs = ws->fullscreen;\n\tstruct sway_container *middle = container_create(NULL);\n\tmiddle->pending.layout = ws->layout;\n\twhile (ws->tiling->length) {\n\t\tstruct sway_container *child = ws->tiling->items[0];\n\t\tcontainer_detach(child);\n\t\tcontainer_add_child(middle, child);\n\t}\n\tworkspace_attach_tiling(ws, middle);\n\tws->fullscreen = fs;\n\treturn middle;\n}\n\nvoid workspace_unwrap_children(struct sway_workspace *ws,\n\t\tstruct sway_container *wrap) {\n\tif (!sway_assert(workspace_is_empty(ws),\n\t\t\t\"target workspace must be empty\")) {\n\t\treturn;\n\t}\n\n\tws->layout = wrap->pending.layout;\n\twhile (wrap->pending.children->length) {\n\t\tstruct sway_container *child = wrap->pending.children->items[0];\n\t\tcontainer_detach(child);\n\t\tworkspace_add_tiling(ws, child);\n\t}\n}\n\nvoid workspace_detach(struct sway_workspace *workspace) {\n\tstruct sway_output *output = workspace->output;\n\tint index = list_find(output->workspaces, workspace);\n\tif (index != -1) {\n\t\tlist_del(output->workspaces, index);\n\t}\n\tworkspace->output = NULL;\n\n\tnode_set_dirty(&workspace->node);\n\tnode_set_dirty(&output->node);\n}\n\nstruct sway_container *workspace_add_tiling(struct sway_workspace *workspace,\n\t\tstruct sway_container *con) {\n\tif (con->pending.workspace) {\n\t\tstruct sway_container *old_parent = con->pending.parent;\n\t\tcontainer_detach(con);\n\t\tif (old_parent) {\n\t\t\tcontainer_reap_empty(old_parent);\n\t\t}\n\t}\n\tif (config->default_layout != L_NONE) {\n\t\tcon = container_split(con, config->default_layout);\n\t}\n\tlist_add(workspace->tiling, con);\n\tcon->pending.workspace = workspace;\n\tcontainer_for_each_child(con, set_workspace, NULL);\n\tcontainer_handle_fullscreen_reparent(con);\n\tworkspace_update_representation(workspace);\n\tnode_set_dirty(&workspace->node);\n\tnode_set_dirty(&con->node);\n\treturn con;\n}\n\nvoid workspace_add_floating(struct sway_workspace *workspace,\n\t\tstruct sway_container *con) {\n\tif (con->pending.workspace) {\n\t\tcontainer_detach(con);\n\t}\n\tlist_add(workspace->floating, con);\n\tcon->pending.workspace = workspace;\n\tcontainer_for_each_child(con, set_workspace, NULL);\n\tcontainer_handle_fullscreen_reparent(con);\n\tnode_set_dirty(&workspace->node);\n\tnode_set_dirty(&con->node);\n}\n\nvoid workspace_insert_tiling_direct(struct sway_workspace *workspace,\n\t\tstruct sway_container *con, int index) {\n\tlist_insert(workspace->tiling, index, con);\n\tcon->pending.workspace = workspace;\n\tcontainer_for_each_child(con, set_workspace, NULL);\n\tcontainer_handle_fullscreen_reparent(con);\n\tworkspace_update_representation(workspace);\n\tnode_set_dirty(&workspace->node);\n\tnode_set_dirty(&con->node);\n}\n\nstruct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,\n\t\tstruct sway_container *con, int index) {\n\tif (con->pending.workspace) {\n\t\tcontainer_detach(con);\n\t}\n\tif (config->default_layout != L_NONE) {\n\t\tcon = container_split(con, config->default_layout);\n\t}\n\tworkspace_insert_tiling_direct(workspace, con, index);\n\treturn con;\n}\n\nbool workspace_has_single_visible_container(struct sway_workspace *ws) {\n\tstruct sway_seat *seat = input_manager_get_default_seat();\n\tstruct sway_container *focus =\n\t\tseat_get_focus_inactive_tiling(seat, ws);\n\tif (focus && !focus->view) {\n\t\tfocus = seat_get_focus_inactive_view(seat, &focus->node);\n\t}\n\treturn (focus && focus->view && view_ancestor_is_only_visible(focus->view));\n}\n\nvoid workspace_add_gaps(struct sway_workspace *ws) {\n\tif (config->smart_gaps == SMART_GAPS_ON\n\t\t\t&& workspace_has_single_visible_container(ws)) {\n\t\tws->current_gaps.top = 0;\n\t\tws->current_gaps.right = 0;\n\t\tws->current_gaps.bottom = 0;\n\t\tws->current_gaps.left = 0;\n\t\treturn;\n\t}\n\n\tif (config->smart_gaps == SMART_GAPS_INVERSE_OUTER\n\t\t\t&& !workspace_has_single_visible_container(ws)) {\n\t\tws->current_gaps.top = 0;\n\t\tws->current_gaps.right = 0;\n\t\tws->current_gaps.bottom = 0;\n\t\tws->current_gaps.left = 0;\n\t} else {\n\t\tws->current_gaps = ws->gaps_outer;\n\t}\n\n\t// Add inner gaps and make sure we don't turn out negative\n\tws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner);\n\tws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner);\n\tws->current_gaps.bottom = fmax(0, ws->current_gaps.bottom + ws->gaps_inner);\n\tws->current_gaps.left = fmax(0, ws->current_gaps.left + ws->gaps_inner);\n\n\t// Now that we have the total gaps calculated we may need to clamp them in\n\t// case they've made the available area too small\n\tif (ws->width - ws->current_gaps.left - ws->current_gaps.right < MIN_SANE_W\n\t\t\t&& ws->current_gaps.left + ws->current_gaps.right > 0) {\n\t\tint total_gap = fmax(0, ws->width - MIN_SANE_W);\n\t\tdouble left_gap_frac = ((double)ws->current_gaps.left /\n\t\t\t((double)ws->current_gaps.left + (double)ws->current_gaps.right));\n\t\tws->current_gaps.left = left_gap_frac * total_gap;\n\t\tws->current_gaps.right = total_gap - ws->current_gaps.left;\n\t}\n\tif (ws->height - ws->current_gaps.top - ws->current_gaps.bottom < MIN_SANE_H\n\t\t\t&& ws->current_gaps.top + ws->current_gaps.bottom > 0) {\n\t\tint total_gap = fmax(0, ws->height - MIN_SANE_H);\n\t\tdouble top_gap_frac = ((double) ws->current_gaps.top /\n\t\t\t((double)ws->current_gaps.top + (double)ws->current_gaps.bottom));\n\t\tws->current_gaps.top = top_gap_frac * total_gap;\n\t\tws->current_gaps.bottom = total_gap - ws->current_gaps.top;\n\t}\n\n\tws->x += ws->current_gaps.left;\n\tws->y += ws->current_gaps.top;\n\tws->width -= ws->current_gaps.left + ws->current_gaps.right;\n\tws->height -= ws->current_gaps.top + ws->current_gaps.bottom;\n}\n\nstruct sway_container *workspace_split(struct sway_workspace *workspace,\n\t\tenum sway_container_layout layout) {\n\tif (workspace->tiling->length == 0) {\n\t\tworkspace->prev_split_layout = workspace->layout;\n\t\tworkspace->layout = layout;\n\t\treturn NULL;\n\t}\n\n\tenum sway_container_layout old_layout = workspace->layout;\n\tstruct sway_container *middle = workspace_wrap_children(workspace);\n\tworkspace->layout = layout;\n\tmiddle->pending.layout = old_layout;\n\n\tstruct sway_seat *seat;\n\twl_list_for_each(seat, &server.input->seats, link) {\n\t\tif (seat_get_focus(seat) == &workspace->node) {\n\t\t\tseat_set_focus(seat, &middle->node);\n\t\t}\n\t}\n\n\treturn middle;\n}\n\nvoid workspace_update_representation(struct sway_workspace *ws) {\n\tsize_t len = container_build_representation(ws->layout, ws->tiling, NULL);\n\tfree(ws->representation);\n\tws->representation = calloc(len + 1, sizeof(char));\n\tif (!sway_assert(ws->representation, \"Unable to allocate title string\")) {\n\t\treturn;\n\t}\n\tcontainer_build_representation(ws->layout, ws->tiling, ws->representation);\n}\n\nvoid workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box) {\n\tbox->x = workspace->x;\n\tbox->y = workspace->y;\n\tbox->width = workspace->width;\n\tbox->height = workspace->height;\n}\n\nstatic void count_tiling_views(struct sway_container *con, void *data) {\n\tif (con->view && !container_is_floating_or_child(con)) {\n\t\tsize_t *count = data;\n\t\t*count += 1;\n\t}\n}\n\nsize_t workspace_num_tiling_views(struct sway_workspace *ws) {\n\tsize_t count = 0;\n\tworkspace_for_each_container(ws, count_tiling_views, &count);\n\treturn count;\n}\n\nstatic void count_sticky_containers(struct sway_container *con, void *data) {\n\tif (container_is_sticky(con)) {\n\t\tsize_t *count = data;\n\t\t*count += 1;\n\t}\n}\n\nsize_t workspace_num_sticky_containers(struct sway_workspace *ws) {\n\tsize_t count = 0;\n\tworkspace_for_each_container(ws, count_sticky_containers, &count);\n\treturn count;\n}\n\nvoid workspace_squash(struct sway_workspace *workspace) {\n\tfor (int i = 0; i < workspace->tiling->length; i++) {\n\t\tstruct sway_container *child = workspace->tiling->items[i];\n\t\ti += container_squash(child);\n\t}\n}\n"
  },
  {
    "path": "sway/xdg_activation_v1.c",
    "content": "#include <wlr/types/wlr_xdg_activation_v1.h>\n#include <wlr/types/wlr_xdg_shell.h>\n#include \"sway/desktop/launcher.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/tree/workspace.h\"\n\nvoid xdg_activation_v1_handle_request_activate(struct wl_listener *listener,\n\t\tvoid *data) {\n\tconst struct wlr_xdg_activation_v1_request_activate_event *event = data;\n\n\tstruct wlr_xdg_surface *xdg_surface =\n\t\twlr_xdg_surface_try_from_wlr_surface(event->surface);\n\tif (xdg_surface == NULL) {\n\t\treturn;\n\t}\n\tstruct sway_view *view = xdg_surface->data;\n\tif (view == NULL) {\n\t\treturn;\n\t}\n\n\tstruct launcher_ctx *ctx = event->token->data;\n\tif (ctx == NULL) {\n\t\treturn;\n\t}\n\n\tif (!xdg_surface->surface->mapped) {\n\t\t// This is a startup notification. If we are tracking it, the data\n\t\t// field is a launcher_ctx.\n\t\tif (ctx->activated) {\n\t\t\t// This ctx has already been activated and cannot be used again\n\t\t\t// for a startup notification. It will be destroyed\n\t\t\treturn;\n\t\t} else {\n\t\t\tctx->activated = true;\n\t\t\tview_assign_ctx(view, ctx);\n\t\t}\n\t\treturn;\n\t}\n\n\t// This is an activation request. If this context is internal we have ctx->seat.\n\tif (ctx->seat) {\n\t\tview_request_activate(view, ctx->seat);\n\t\treturn;\n\t}\n\n\t// Otherwise, activate if passed from another focused client\n\tif (ctx->token->seat && ctx->had_focused_surface) {\n\t\tview_request_activate(view, ctx->token->seat->data);\n\t} else {\n\t\t// The token is valid, but cannot be used to activate a window\n\t\tview_request_urgent(view);\n\t}\n}\n\nvoid xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) {\n\tstruct wlr_xdg_activation_token_v1 *token = data;\n\tstruct sway_seat *seat = token->seat ? token->seat->data :\n\t\tinput_manager_current_seat();\n\n\tstruct sway_workspace *ws = seat_get_focused_workspace(seat);\n\tif (ws) {\n\t\tlauncher_ctx_create(token, &ws->node);\n\t\treturn;\n\t}\n}\n"
  },
  {
    "path": "sway/xdg_decoration.c",
    "content": "#include <stdlib.h>\n#include \"sway/desktop/transaction.h\"\n#include \"sway/server.h\"\n#include \"sway/tree/arrange.h\"\n#include \"sway/tree/view.h\"\n#include \"sway/xdg_decoration.h\"\n#include \"log.h\"\n\nstatic void xdg_decoration_handle_destroy(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_xdg_decoration *deco =\n\t\twl_container_of(listener, deco, destroy);\n\tif (deco->view) {\n\t\tdeco->view->xdg_decoration = NULL;\n\t}\n\twl_list_remove(&deco->destroy.link);\n\twl_list_remove(&deco->request_mode.link);\n\twl_list_remove(&deco->link);\n\tfree(deco);\n}\n\nstatic void xdg_decoration_handle_request_mode(struct wl_listener *listener,\n\t\tvoid *data) {\n\tstruct sway_xdg_decoration *deco =\n\t\twl_container_of(listener, deco, request_mode);\n\tset_xdg_decoration_mode(deco);\n}\n\nvoid handle_xdg_decoration(struct wl_listener *listener, void *data) {\n\tstruct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data;\n\tstruct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data;\n\n\tstruct sway_xdg_decoration *deco = calloc(1, sizeof(*deco));\n\tif (deco == NULL) {\n\t\treturn;\n\t}\n\n\tdeco->view = &xdg_shell_view->view;\n\tdeco->view->xdg_decoration = deco;\n\tdeco->wlr_xdg_decoration = wlr_deco;\n\n\twl_signal_add(&wlr_deco->events.destroy, &deco->destroy);\n\tdeco->destroy.notify = xdg_decoration_handle_destroy;\n\n\twl_signal_add(&wlr_deco->events.request_mode, &deco->request_mode);\n\tdeco->request_mode.notify = xdg_decoration_handle_request_mode;\n\n\twl_list_insert(&server.xdg_decorations, &deco->link);\n\n\tset_xdg_decoration_mode(deco);\n}\n\nstruct sway_xdg_decoration *xdg_decoration_from_surface(\n\t\tstruct wlr_surface *surface) {\n\tstruct sway_xdg_decoration *deco;\n\twl_list_for_each(deco, &server.xdg_decorations, link) {\n\t\tif (deco->wlr_xdg_decoration->toplevel->base->surface == surface) {\n\t\t\treturn deco;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nvoid set_xdg_decoration_mode(struct sway_xdg_decoration *deco) {\n\tstruct sway_view *view = deco->view;\n\tenum wlr_xdg_toplevel_decoration_v1_mode mode =\n\t\tWLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;\n\tenum wlr_xdg_toplevel_decoration_v1_mode client_mode =\n\t\tdeco->wlr_xdg_decoration->requested_mode;\n\n\tbool floating;\n\tif (view->container) {\n\t\tfloating = container_is_floating(view->container);\n\t\tbool csd = false;\n\t\tcsd = client_mode ==\n\t\t\tWLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;\n\t\tview_update_csd_from_client(view, csd);\n\t\tarrange_container(view->container);\n\t\ttransaction_commit_dirty();\n\t} else {\n\t\tfloating = view->impl->wants_floating &&\n\t\t\tview->impl->wants_floating(view);\n\t}\n\n\tif (floating && client_mode) {\n\t\tmode = client_mode;\n\t}\n\n\tif (view->wlr_xdg_toplevel->base->initialized) {\n\t\twlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, mode);\n\t}\n}\n"
  },
  {
    "path": "sway.desktop",
    "content": "[Desktop Entry]\nName=Sway\nComment=An i3-compatible Wayland compositor\nExec=sway\nType=Application\nDesktopNames=sway;wlroots\n"
  },
  {
    "path": "swaybar/bar.c",
    "content": "#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <poll.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <wayland-client.h>\n#include <wayland-cursor.h>\n#include \"config.h\"\n#include \"swaybar/bar.h\"\n#include \"swaybar/config.h\"\n#include \"swaybar/i3bar.h\"\n#include \"swaybar/input.h\"\n#include \"swaybar/ipc.h\"\n#include \"swaybar/status_line.h\"\n#include \"swaybar/render.h\"\n#if HAVE_TRAY\n#include \"swaybar/tray/tray.h\"\n#endif\n#include \"ipc-client.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"loop.h\"\n#include \"pool-buffer.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n#include \"xdg-output-unstable-v1-client-protocol.h\"\n\nvoid free_workspaces(struct wl_list *list) {\n\tstruct swaybar_workspace *ws, *tmp;\n\twl_list_for_each_safe(ws, tmp, list, link) {\n\t\twl_list_remove(&ws->link);\n\t\tfree(ws->name);\n\t\tfree(ws->label);\n\t\tfree(ws);\n\t}\n}\n\nstatic void swaybar_output_free(struct swaybar_output *output) {\n\tif (!output) {\n\t\treturn;\n\t}\n\tsway_log(SWAY_DEBUG, \"Removing output %s\", output->name);\n\tif (output->layer_surface != NULL) {\n\t\tzwlr_layer_surface_v1_destroy(output->layer_surface);\n\t}\n\tif (output->surface != NULL) {\n\t\twl_surface_destroy(output->surface);\n\t}\n\twl_output_destroy(output->output);\n\tdestroy_buffer(&output->buffers[0]);\n\tdestroy_buffer(&output->buffers[1]);\n\tfree_hotspots(&output->hotspots);\n\tfree_workspaces(&output->workspaces);\n\twl_list_remove(&output->link);\n\tfree(output->name);\n\tfree(output->identifier);\n\tfree(output);\n}\n\nstatic void set_output_dirty(struct swaybar_output *output) {\n\tif (output->frame_scheduled) {\n\t\toutput->dirty = true;\n\t} else if (output->surface) {\n\t\trender_frame(output);\n\t}\n}\n\nstatic void layer_surface_configure(void *data,\n\t\tstruct zwlr_layer_surface_v1 *surface,\n\t\tuint32_t serial, uint32_t width, uint32_t height) {\n\tstruct swaybar_output *output = data;\n\toutput->width = width;\n\toutput->height = height;\n\tzwlr_layer_surface_v1_ack_configure(surface, serial);\n\tset_output_dirty(output);\n}\n\nstatic void layer_surface_closed(void *_output,\n\t\tstruct zwlr_layer_surface_v1 *surface) {\n\tstruct swaybar_output *output = _output;\n\tswaybar_output_free(output);\n}\n\nstatic const struct zwlr_layer_surface_v1_listener layer_surface_listener = {\n\t.configure = layer_surface_configure,\n\t.closed = layer_surface_closed,\n};\n\nstatic void add_layer_surface(struct swaybar_output *output) {\n\tif (output->layer_surface) {\n\t\treturn;\n\t}\n\tstruct swaybar *bar = output->bar;\n\n\tstruct swaybar_config *config = bar->config;\n\tbool hidden = strcmp(config->mode, \"hide\") == 0;\n\tbool overlay = !hidden && strcmp(config->mode, \"overlay\") == 0;\n\toutput->layer_surface = zwlr_layer_shell_v1_get_layer_surface(\n\t\t\tbar->layer_shell, output->surface, output->output,\n\t\t\thidden || overlay ? ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY :\n\t\t\tZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, \"panel\");\n\tassert(output->layer_surface);\n\tzwlr_layer_surface_v1_add_listener(output->layer_surface,\n\t\t\t&layer_surface_listener, output);\n\n\tif (overlay) {\n\t\t// Empty input region\n\t\tstruct wl_region *region = wl_compositor_create_region(bar->compositor);\n\t\twl_surface_set_input_region(output->surface, region);\n\t\twl_region_destroy(region);\n\t}\n\n\tzwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position);\n\tif (hidden || overlay) {\n\t\tzwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1);\n\t}\n}\n\nvoid destroy_layer_surface(struct swaybar_output *output) {\n\tif (!output->layer_surface) {\n\t\treturn;\n\t}\n\tzwlr_layer_surface_v1_destroy(output->layer_surface);\n\twl_surface_attach(output->surface, NULL, 0, 0); // detach buffer\n\toutput->layer_surface = NULL;\n\toutput->width = 0;\n\toutput->frame_scheduled = false;\n}\n\nvoid set_bar_dirty(struct swaybar *bar) {\n\tstruct swaybar_output *output;\n\twl_list_for_each(output, &bar->outputs, link) {\n\t\tset_output_dirty(output);\n\t}\n}\n\nbool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {\n\tstruct swaybar_config *config = bar->config;\n\tbool visible = !(strcmp(config->mode, \"invisible\") == 0 ||\n\t\t(strcmp(config->mode, config->hidden_state) == 0 // both \"hide\"\n\t\t\t&& !bar->visible_by_modifier && !bar->visible_by_urgency\n\t\t\t&& !bar->visible_by_mode));\n\n\t// Create/destroy layer surfaces as needed\n\tstruct swaybar_output *output;\n\twl_list_for_each(output, &bar->outputs, link) {\n\t\t// When moving to a different layer, we need to destroy and re-create\n\t\t// the layer surface\n\t\tif (!visible || moving_layer) {\n\t\t\tdestroy_layer_surface(output);\n\t\t}\n\n\t\tif (visible) {\n\t\t\tadd_layer_surface(output);\n\t\t}\n\t}\n\tset_bar_dirty(bar);\n\n\tif (visible != bar->visible) {\n\t\tbar->visible = visible;\n\n\t\tif (bar->status) {\n\t\t\tsway_log(SWAY_DEBUG, \"Sending %s signal to status command\",\n\t\t\t\t\tvisible ? \"cont\" : \"stop\");\n\t\t\tkill(-bar->status->pid, visible ?\n\t\t\t\t\tbar->status->cont_signal : bar->status->stop_signal);\n\t\t}\n\t}\n\n\treturn visible;\n}\n\nstatic bool bar_uses_output(struct swaybar_output *output) {\n\tif (wl_list_empty(&output->bar->config->outputs)) {\n\t\treturn true;\n\t}\n\tchar *identifier = output->identifier;\n\tstruct config_output *coutput;\n\twl_list_for_each(coutput, &output->bar->config->outputs, link) {\n\t\tif (strcmp(coutput->name, output->name) == 0 ||\n\t\t\t\t(identifier && strcmp(coutput->name, identifier) == 0)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic void output_geometry(void *data, struct wl_output *wl_output, int32_t x,\n\t\tint32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,\n\t\tconst char *make, const char *model, int32_t transform) {\n\tstruct swaybar_output *output = data;\n\toutput->subpixel = subpixel;\n}\n\nstatic void output_mode(void *data, struct wl_output *wl_output, uint32_t flags,\n\t\tint32_t width, int32_t height, int32_t refresh) {\n\t// Who cares\n}\n\nstatic void output_done(void *data, struct wl_output *wl_output) {\n\tstruct swaybar_output *output = data;\n\tset_output_dirty(output);\n}\n\nstatic void output_scale(void *data, struct wl_output *wl_output,\n\t\tint32_t factor) {\n\tstruct swaybar_output *output = data;\n\toutput->scale = factor;\n\n\tbool render = false;\n\tstruct swaybar_seat *seat;\n\twl_list_for_each(seat, &output->bar->seats, link) {\n\t\tif (output == seat->pointer.current) {\n\t\t\tupdate_cursor(seat);\n\t\t\trender = true;\n\t\t}\n\t};\n\tif (render) {\n\t\trender_frame(output);\n\t}\n}\n\nstatic const struct wl_output_listener output_listener = {\n\t.geometry = output_geometry,\n\t.mode = output_mode,\n\t.done = output_done,\n\t.scale = output_scale,\n};\n\nstatic void xdg_output_handle_logical_position(void *data,\n\t\tstruct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) {\n\tstruct swaybar_output *output = data;\n\toutput->output_x = x;\n\toutput->output_y = y;\n}\n\nstatic void xdg_output_handle_logical_size(void *data,\n\t\tstruct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) {\n\tstruct swaybar_output *output = data;\n\toutput->output_height = height;\n\toutput->output_width = width;\n}\n\nstatic void xdg_output_handle_done(void *data,\n\t\tstruct zxdg_output_v1 *xdg_output) {\n\tstruct swaybar_output *output = data;\n\tstruct swaybar *bar = output->bar;\n\n\tif (!wl_list_empty(&output->link)) {\n\t\treturn;\n\t}\n\n\tassert(output->name != NULL);\n\tif (!bar_uses_output(output)) {\n\t\twl_list_remove(&output->link);\n\t\twl_list_insert(&bar->unused_outputs, &output->link);\n\t\treturn;\n\t}\n\n\twl_list_remove(&output->link);\n\twl_list_insert(&bar->outputs, &output->link);\n\n\toutput->surface = wl_compositor_create_surface(bar->compositor);\n\tassert(output->surface);\n\n\tdetermine_bar_visibility(bar, false);\n\n\tif (bar->running && bar->config->workspace_buttons) {\n\t\tipc_get_workspaces(bar);\n\t}\n}\n\nstatic void xdg_output_handle_name(void *data,\n\t\tstruct zxdg_output_v1 *xdg_output, const char *name) {\n\tstruct swaybar_output *output = data;\n\tfree(output->name);\n\toutput->name = strdup(name);\n}\n\nstatic void xdg_output_handle_description(void *data,\n\t\tstruct zxdg_output_v1 *xdg_output, const char *description) {\n\t// wlroots currently sets the description to `make model serial (name)`\n\t// If this changes in the future, this will need to be modified.\n\tstruct swaybar_output *output = data;\n\tfree(output->identifier);\n\toutput->identifier = NULL;\n\tchar *paren = strrchr(description, '(');\n\tif (paren) {\n\t\tsize_t length = paren - description;\n\t\toutput->identifier = malloc(length);\n\t\tif (!output->identifier) {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to allocate output identifier\");\n\t\t\treturn;\n\t\t}\n\t\tstrncpy(output->identifier, description, length);\n\t\toutput->identifier[length - 1] = '\\0';\n\t}\n}\n\nstatic const struct zxdg_output_v1_listener xdg_output_listener = {\n\t.logical_position = xdg_output_handle_logical_position,\n\t.logical_size = xdg_output_handle_logical_size,\n\t.done = xdg_output_handle_done,\n\t.name = xdg_output_handle_name,\n\t.description = xdg_output_handle_description,\n};\n\nstatic void add_xdg_output(struct swaybar_output *output) {\n\tif (output->xdg_output != NULL) {\n\t\treturn;\n\t}\n\tassert(output->bar->xdg_output_manager != NULL);\n\toutput->xdg_output = zxdg_output_manager_v1_get_xdg_output(\n\t\toutput->bar->xdg_output_manager, output->output);\n\tzxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,\n\t\toutput);\n}\n\nstatic void handle_global(void *data, struct wl_registry *registry,\n\t\tuint32_t name, const char *interface, uint32_t version) {\n\tstruct swaybar *bar = data;\n\tif (strcmp(interface, wl_compositor_interface.name) == 0) {\n\t\tbar->compositor = wl_registry_bind(registry, name,\n\t\t\t\t&wl_compositor_interface, 4);\n\t} else if (strcmp(interface, wl_seat_interface.name) == 0) {\n\t\tstruct swaybar_seat *seat = calloc(1, sizeof(struct swaybar_seat));\n\t\tif (!seat) {\n\t\t\tsway_abort(\"Failed to allocate swaybar_seat\");\n\t\t\treturn;\n\t\t}\n\t\tseat->bar = bar;\n\t\tseat->wl_name = name;\n\t\tseat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 5);\n\t\twl_seat_add_listener(seat->wl_seat, &seat_listener, seat);\n\t\twl_list_insert(&bar->seats, &seat->link);\n\t} else if (strcmp(interface, wl_shm_interface.name) == 0) {\n\t\tbar->shm = wl_registry_bind(registry, name,\n\t\t\t\t&wl_shm_interface, 1);\n\t} else if (strcmp(interface, wl_output_interface.name) == 0) {\n\t\tstruct swaybar_output *output =\n\t\t\tcalloc(1, sizeof(struct swaybar_output));\n\t\toutput->bar = bar;\n\t\toutput->output = wl_registry_bind(registry, name,\n\t\t\t\t&wl_output_interface, 3);\n\t\twl_output_add_listener(output->output, &output_listener, output);\n\t\toutput->scale = 1;\n\t\toutput->wl_name = name;\n\t\twl_list_init(&output->workspaces);\n\t\twl_list_init(&output->hotspots);\n\t\twl_list_init(&output->link);\n\t\tif (bar->xdg_output_manager != NULL) {\n\t\t\tadd_xdg_output(output);\n\t\t}\n\t} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {\n\t\tbar->layer_shell = wl_registry_bind(\n\t\t\t\tregistry, name, &zwlr_layer_shell_v1_interface, 1);\n\t} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {\n\t\tbar->xdg_output_manager = wl_registry_bind(registry, name,\n\t\t\t&zxdg_output_manager_v1_interface, 2);\n\t} else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {\n\t\tbar->cursor_shape_manager = wl_registry_bind(registry, name,\n\t\t\t&wp_cursor_shape_manager_v1_interface, 1);\n\t}\n}\n\nstatic void handle_global_remove(void *data, struct wl_registry *registry,\n\t\tuint32_t name) {\n\tstruct swaybar *bar = data;\n\tstruct swaybar_output *output, *tmp;\n\twl_list_for_each_safe(output, tmp, &bar->outputs, link) {\n\t\tif (output->wl_name == name) {\n\t\t\tswaybar_output_free(output);\n\t\t\treturn;\n\t\t}\n\t}\n\twl_list_for_each_safe(output, tmp, &bar->unused_outputs, link) {\n\t\tif (output->wl_name == name) {\n\t\t\tswaybar_output_free(output);\n\t\t\treturn;\n\t\t}\n\t}\n\tstruct swaybar_seat *seat, *tmp_seat;\n\twl_list_for_each_safe(seat, tmp_seat, &bar->seats, link) {\n\t\tif (seat->wl_name == name) {\n\t\t\tswaybar_seat_free(seat);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic const struct wl_registry_listener registry_listener = {\n\t.global = handle_global,\n\t.global_remove = handle_global_remove,\n};\n\nbool bar_setup(struct swaybar *bar, const char *socket_path) {\n\tbar->visible = true;\n\tbar->config = init_config();\n\twl_list_init(&bar->outputs);\n\twl_list_init(&bar->unused_outputs);\n\twl_list_init(&bar->seats);\n\tbar->eventloop = loop_create();\n\n\tbar->ipc_socketfd = ipc_open_socket(socket_path);\n\tbar->ipc_event_socketfd = ipc_open_socket(socket_path);\n\tif (!ipc_initialize(bar)) {\n\t\treturn false;\n\t}\n\n\tbar->display = wl_display_connect(NULL);\n\tif (!bar->display) {\n\t\tsway_abort(\"Unable to connect to the compositor. \"\n\t\t\t\t\"If your compositor is running, check or set the \"\n\t\t\t\t\"WAYLAND_DISPLAY environment variable.\");\n\t}\n\n\tstruct wl_registry *registry = wl_display_get_registry(bar->display);\n\twl_registry_add_listener(registry, &registry_listener, bar);\n\twl_display_roundtrip(bar->display);\n\tassert(bar->compositor && bar->layer_shell && bar->shm &&\n\t\tbar->xdg_output_manager);\n\n\t// Second roundtrip for xdg-output\n\twl_display_roundtrip(bar->display);\n\n\tif (!bar->cursor_shape_manager) {\n\t\tstruct swaybar_seat *seat;\n\t\twl_list_for_each(seat, &bar->seats, link) {\n\t\t\tstruct swaybar_pointer *pointer = &seat->pointer;\n\t\t\tif (!pointer) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpointer->cursor_surface =\n\t\t\t\twl_compositor_create_surface(bar->compositor);\n\t\t\tassert(pointer->cursor_surface);\n\t\t}\n\t}\n\n\tif (bar->config->status_command) {\n\t\tbar->status = status_line_init(bar->config->status_command);\n\t\tbar->status->bar = bar;\n\t}\n\n#if HAVE_TRAY\n\tif (!bar->config->tray_hidden) {\n\t\tbar->tray = create_tray(bar);\n\t}\n#endif\n\n\tif (bar->config->workspace_buttons) {\n\t\tipc_get_workspaces(bar);\n\t}\n\tdetermine_bar_visibility(bar, false);\n\treturn true;\n}\n\nstatic void display_in(int fd, short mask, void *data) {\n\tstruct swaybar *bar = data;\n\tif (mask & (POLLHUP | POLLERR)) {\n\t\tif (mask & POLLERR) {\n\t\t\tsway_log(SWAY_ERROR, \"Wayland display poll error\");\n\t\t}\n\t\tbar->running = false;\n\t\treturn;\n\t}\n\tif (wl_display_dispatch(bar->display) == -1) {\n\t\tsway_log(SWAY_ERROR, \"wl_display_dispatch failed\");\n\t\tbar->running = false;\n\t}\n}\n\nstatic void ipc_in(int fd, short mask, void *data) {\n\tstruct swaybar *bar = data;\n\tif (mask & (POLLHUP | POLLERR)) {\n\t\tif (mask & POLLERR) {\n\t\t\tsway_log(SWAY_ERROR, \"IPC poll error\");\n\t\t}\n\t\tbar->running = false;\n\t\treturn;\n\t}\n\tif (handle_ipc_readable(bar)) {\n\t\tset_bar_dirty(bar);\n\t}\n}\n\nvoid status_in(int fd, short mask, void *data) {\n\tstruct swaybar *bar = data;\n\tif (mask & (POLLHUP | POLLERR)) {\n\t\tstatus_error(bar->status, \"[error reading from status command]\");\n\t\tset_bar_dirty(bar);\n\t\tloop_remove_fd(bar->eventloop, fd);\n\t} else if (status_handle_readable(bar->status)) {\n\t\tset_bar_dirty(bar);\n\t}\n}\n\nvoid bar_run(struct swaybar *bar) {\n\tloop_add_fd(bar->eventloop, wl_display_get_fd(bar->display), POLLIN,\n\t\t\tdisplay_in, bar);\n\tloop_add_fd(bar->eventloop, bar->ipc_event_socketfd, POLLIN, ipc_in, bar);\n\tif (bar->status) {\n\t\tloop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN,\n\t\t\t\tstatus_in, bar);\n\t}\n#if HAVE_TRAY\n\tif (bar->tray) {\n\t\tloop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar);\n\t}\n#endif\n\twhile (bar->running) {\n\t\terrno = 0;\n\t\tif (wl_display_flush(bar->display) == -1 && errno != EAGAIN) {\n\t\t\tbreak;\n\t\t}\n\t\tloop_poll(bar->eventloop);\n\t}\n}\n\nstatic void free_outputs(struct wl_list *list) {\n\tstruct swaybar_output *output, *tmp;\n\twl_list_for_each_safe(output, tmp, list, link) {\n\t\tswaybar_output_free(output);\n\t}\n}\n\nstatic void free_seats(struct wl_list *list) {\n\tstruct swaybar_seat *seat, *tmp;\n\twl_list_for_each_safe(seat, tmp, list, link) {\n\t\tswaybar_seat_free(seat);\n\t}\n}\n\nvoid bar_teardown(struct swaybar *bar) {\n#if HAVE_TRAY\n\tdestroy_tray(bar->tray);\n#endif\n\tfree_outputs(&bar->outputs);\n\tfree_outputs(&bar->unused_outputs);\n\tfree_seats(&bar->seats);\n\tif (bar->config) {\n\t\tfree_config(bar->config);\n\t}\n\tclose(bar->ipc_event_socketfd);\n\tclose(bar->ipc_socketfd);\n\tif (bar->status) {\n\t\tstatus_line_free(bar->status);\n\t}\n\tfree(bar->id);\n\tfree(bar->mode);\n}\n"
  },
  {
    "path": "swaybar/config.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"swaybar/config.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n#include \"config.h\"\n#include \"stringop.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nuint32_t parse_position(const char *position) {\n\tuint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |\n\t\tZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;\n\tif (strcmp(\"top\", position) == 0) {\n\t\treturn ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | horiz;\n\t} else if (strcmp(\"bottom\", position) == 0) {\n\t\treturn ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;\n\t} else {\n\t\tsway_log(SWAY_ERROR, \"Invalid position: %s, defaulting to bottom\", position);\n\t\treturn ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;\n\t}\n}\n\nstruct swaybar_config *init_config(void) {\n\tstruct swaybar_config *config = calloc(1, sizeof(struct swaybar_config));\n\tconfig->status_command = NULL;\n\tconfig->pango_markup = false;\n\tconfig->position = parse_position(\"bottom\");\n\tconfig->font_description = pango_font_description_from_string(\"monospace 10\");\n\tconfig->mode = strdup(\"dock\");\n\tconfig->hidden_state = strdup(\"hide\");\n\tconfig->sep_symbol = NULL;\n\tconfig->strip_workspace_numbers = false;\n\tconfig->strip_workspace_name = false;\n\tconfig->binding_mode_indicator = true;\n\tconfig->wrap_scroll = false;\n\tconfig->workspace_buttons = true;\n\tconfig->workspace_min_width = 0;\n\tconfig->bindings = create_list();\n\twl_list_init(&config->outputs);\n\tconfig->status_padding = 1;\n\tconfig->status_edge_padding = 3;\n\n\t/* height */\n\tconfig->height = 0;\n\n\t/* gaps */\n\tconfig->gaps.top = 0;\n\tconfig->gaps.right = 0;\n\tconfig->gaps.bottom = 0;\n\tconfig->gaps.left = 0;\n\n\t/* colors */\n\tconfig->colors.background = 0x000000FF;\n\tconfig->colors.focused_background = 0x000000FF;\n\tconfig->colors.statusline = 0xFFFFFFFF;\n\tconfig->colors.focused_statusline = 0xFFFFFFFF;\n\tconfig->colors.separator = 0x666666FF;\n\n\tconfig->colors.focused_workspace.border = 0x4C7899FF;\n\tconfig->colors.focused_workspace.background = 0x285577FF;\n\tconfig->colors.focused_workspace.text = 0xFFFFFFFF;\n\n\tconfig->colors.active_workspace.border = 0x333333FF;\n\tconfig->colors.active_workspace.background = 0x5F676AFF;\n\tconfig->colors.active_workspace.text = 0xFFFFFFFF;\n\n\tconfig->colors.inactive_workspace.border = 0x333333FF;\n\tconfig->colors.inactive_workspace.background = 0x222222FF;\n\tconfig->colors.inactive_workspace.text = 0x888888FF;\n\n\tconfig->colors.urgent_workspace.border = 0x2F343AFF;\n\tconfig->colors.urgent_workspace.background = 0x900000FF;\n\tconfig->colors.urgent_workspace.text = 0xFFFFFFFF;\n\n\tconfig->colors.binding_mode.border = 0x2F343AFF;\n\tconfig->colors.binding_mode.background = 0x900000FF;\n\tconfig->colors.binding_mode.text = 0xFFFFFFFF;\n\n#if HAVE_TRAY\n\tconfig->tray_padding = 2;\n\twl_list_init(&config->tray_bindings);\n#endif\n\n\treturn config;\n}\n\nvoid free_binding(struct swaybar_binding *binding) {\n\tif (!binding) {\n\t\treturn;\n\t}\n\tfree(binding->command);\n\tfree(binding);\n}\n\n#if HAVE_TRAY\nvoid free_tray_binding(struct tray_binding *binding) {\n\tif (!binding) {\n\t\treturn;\n\t}\n\tfree(binding->command);\n\tfree(binding);\n}\n#endif\n\nvoid free_config(struct swaybar_config *config) {\n\tfree(config->status_command);\n\tpango_font_description_free(config->font_description);\n\tfree(config->mode);\n\tfree(config->hidden_state);\n\tfree(config->sep_symbol);\n\tfor (int i = 0; i < config->bindings->length; i++) {\n\t\tstruct swaybar_binding *binding = config->bindings->items[i];\n\t\tfree_binding(binding);\n\t}\n\tlist_free(config->bindings);\n\tstruct config_output *coutput, *tmp;\n\twl_list_for_each_safe(coutput, tmp, &config->outputs, link) {\n\t\twl_list_remove(&coutput->link);\n\t\tfree(coutput->name);\n\t\tfree(coutput);\n\t}\n#if HAVE_TRAY\n\tlist_free_items_and_destroy(config->tray_outputs);\n\n\tstruct tray_binding *tray_bind = NULL, *tmp_tray_bind = NULL;\n\twl_list_for_each_safe(tray_bind, tmp_tray_bind, &config->tray_bindings,\n\t\t\tlink) {\n\t\twl_list_remove(&tray_bind->link);\n\t\tfree_tray_binding(tray_bind);\n\t}\n\n\tfree(config->icon_theme);\n#endif\n\tfree(config);\n}\n"
  },
  {
    "path": "swaybar/i3bar.c",
    "content": "#include <json.h>\n#include <linux/input-event-codes.h>\n#include <ctype.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include \"log.h\"\n#include \"swaybar/bar.h\"\n#include \"swaybar/config.h\"\n#include \"swaybar/i3bar.h\"\n#include \"swaybar/input.h\"\n#include \"swaybar/status_line.h\"\n\nvoid i3bar_block_unref(struct i3bar_block *block) {\n\tif (block == NULL) {\n\t\treturn;\n\t}\n\n\tif (--block->ref_count == 0) {\n\t\tfree(block->full_text);\n\t\tfree(block->short_text);\n\t\tfree(block->align);\n\t\tfree(block->min_width_str);\n\t\tfree(block->name);\n\t\tfree(block->instance);\n\t\tfree(block);\n\t}\n}\n\nstatic bool i3bar_parse_json_color(json_object *json, uint32_t *color) {\n\tif (!json) {\n\t\treturn false;\n\t}\n\n\tconst char *hexstring = json_object_get_string(json);\n\tbool color_set = parse_color(hexstring, color);\n\tif (!color_set) {\n\t\tsway_log(SWAY_ERROR, \"Ignoring invalid block hexadecimal color string: %s\", hexstring);\n\t}\n\treturn color_set;\n}\n\nstatic void i3bar_parse_json(struct status_line *status,\n\t\tstruct json_object *json_array) {\n\tstruct i3bar_block *block, *tmp;\n\twl_list_for_each_safe(block, tmp, &status->blocks, link) {\n\t\twl_list_remove(&block->link);\n\t\ti3bar_block_unref(block);\n\t}\n\tfor (size_t i = 0; i < json_object_array_length(json_array); ++i) {\n\t\tjson_object *full_text, *short_text, *color, *min_width, *align, *urgent;\n\t\tjson_object *name, *instance, *separator, *separator_block_width;\n\t\tjson_object *background, *border, *border_top, *border_bottom;\n\t\tjson_object *border_left, *border_right, *markup;\n\t\tjson_object *json = json_object_array_get_idx(json_array, i);\n\t\tif (!json) {\n\t\t\tcontinue;\n\t\t}\n\t\tjson_object_object_get_ex(json, \"full_text\", &full_text);\n\t\tjson_object_object_get_ex(json, \"short_text\", &short_text);\n\t\tjson_object_object_get_ex(json, \"color\", &color);\n\t\tjson_object_object_get_ex(json, \"min_width\", &min_width);\n\t\tjson_object_object_get_ex(json, \"align\", &align);\n\t\tjson_object_object_get_ex(json, \"urgent\", &urgent);\n\t\tjson_object_object_get_ex(json, \"name\", &name);\n\t\tjson_object_object_get_ex(json, \"instance\", &instance);\n\t\tjson_object_object_get_ex(json, \"markup\", &markup);\n\t\tjson_object_object_get_ex(json, \"separator\", &separator);\n\t\tjson_object_object_get_ex(json, \"separator_block_width\", &separator_block_width);\n\t\tjson_object_object_get_ex(json, \"background\", &background);\n\t\tjson_object_object_get_ex(json, \"border\", &border);\n\t\tjson_object_object_get_ex(json, \"border_top\", &border_top);\n\t\tjson_object_object_get_ex(json, \"border_bottom\", &border_bottom);\n\t\tjson_object_object_get_ex(json, \"border_left\", &border_left);\n\t\tjson_object_object_get_ex(json, \"border_right\", &border_right);\n\n\t\tstruct i3bar_block *block = calloc(1, sizeof(struct i3bar_block));\n\t\tblock->ref_count = 1;\n\t\tblock->full_text = full_text ?\n\t\t\tstrdup(json_object_get_string(full_text)) : NULL;\n\t\tblock->short_text = short_text ?\n\t\t\tstrdup(json_object_get_string(short_text)) : NULL;\n\t\tblock->color_set = i3bar_parse_json_color(color, &block->color);\n\t\tif (min_width) {\n\t\t\tjson_type type = json_object_get_type(min_width);\n\t\t\tif (type == json_type_int) {\n\t\t\t\tblock->min_width = json_object_get_int(min_width);\n\t\t\t} else if (type == json_type_string) {\n\t\t\t\t/* the width will be calculated when rendering */\n\t\t\t\tblock->min_width_str = strdup(json_object_get_string(min_width));\n\t\t\t}\n\t\t}\n\t\tblock->align = strdup(align ? json_object_get_string(align) : \"left\");\n\t\tblock->urgent = urgent ? json_object_get_int(urgent) : false;\n\t\tblock->name = name ? strdup(json_object_get_string(name)) : NULL;\n\t\tblock->instance = instance ?\n\t\t\tstrdup(json_object_get_string(instance)) : NULL;\n\t\tif (markup) {\n\t\t\tblock->markup = false;\n\t\t\tconst char *markup_str = json_object_get_string(markup);\n\t\t\tif (strcmp(markup_str, \"pango\") == 0) {\n\t\t\t\tblock->markup = true;\n\t\t\t}\n\t\t}\n\t\tblock->separator = separator ? json_object_get_int(separator) : true;\n\t\tblock->separator_block_width = separator_block_width ?\n\t\t\tjson_object_get_int(separator_block_width) : 9;\n\t\t// Airblader features\n\t\ti3bar_parse_json_color(background, &block->background);\n\t\tblock->border_set = i3bar_parse_json_color(border, &block->border);\n\t\tblock->border_top = border_top ? json_object_get_int(border_top) : 1;\n\t\tblock->border_bottom = border_bottom ?\n\t\t\tjson_object_get_int(border_bottom) : 1;\n\t\tblock->border_left = border_left ? json_object_get_int(border_left) : 1;\n\t\tblock->border_right = border_right ?\n\t\t\tjson_object_get_int(border_right) : 1;\n\t\twl_list_insert(&status->blocks, &block->link);\n\t}\n}\n\nbool i3bar_handle_readable(struct status_line *status) {\n\twhile (!status->started) { // look for opening bracket\n\t\tfor (size_t c = 0; c < status->buffer_index; ++c) {\n\t\t\tif (status->buffer[c] == '[') {\n\t\t\t\tstatus->started = true;\n\t\t\t\tstatus->buffer_index -= ++c;\n\t\t\t\tmemmove(status->buffer, &status->buffer[c], status->buffer_index);\n\t\t\t\tbreak;\n\t\t\t} else if (!isspace(status->buffer[c])) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Invalid i3bar json: expected '[' but encountered '%c'\",\n\t\t\t\t\t\tstatus->buffer[c]);\n\t\t\t\tstatus_error(status, \"[invalid i3bar json]\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (status->started) {\n\t\t\tbreak;\n\t\t}\n\n\t\terrno = 0;\n\t\tssize_t read_bytes = read(status->read_fd, status->buffer, status->buffer_size);\n\t\tif (read_bytes > -1) {\n\t\t\tstatus->buffer_index = read_bytes;\n\t\t} else if (errno == EAGAIN) {\n\t\t\treturn false;\n\t\t} else {\n\t\t\tstatus_error(status, \"[error reading from status command]\");\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tstruct json_object *last_object = NULL;\n\tstruct json_object *test_object;\n\tsize_t buffer_pos = 0;\n\twhile (true) {\n\t\t// since the incoming stream is an infinite array\n\t\t// parsing is split into two parts\n\t\t// first, attempt to parse the current object, reading more if the\n\t\t// parser indicates that the current object is incomplete, and failing\n\t\t// if the parser fails\n\t\t// second, look for separating comma, ignoring whitespace, failing if\n\t\t// any other characters are encountered\n\t\tif (status->expecting_comma) {\n\t\t\tfor (; buffer_pos < status->buffer_index; ++buffer_pos) {\n\t\t\t\tif (status->buffer[buffer_pos] == ',') {\n\t\t\t\t\tstatus->expecting_comma = false;\n\t\t\t\t\t++buffer_pos;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (!isspace(status->buffer[buffer_pos])) {\n\t\t\t\t\tsway_log(SWAY_DEBUG, \"Invalid i3bar json: expected ',' but encountered '%c'\",\n\t\t\t\t\t\t\tstatus->buffer[buffer_pos]);\n\t\t\t\t\tstatus_error(status, \"[invalid i3bar json]\");\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (buffer_pos < status->buffer_index) {\n\t\t\t\tcontinue; // look for new object without reading more input\n\t\t\t}\n\t\t\tbuffer_pos = status->buffer_index = 0;\n\t\t} else {\n\t\t\ttest_object = json_tokener_parse_ex(status->tokener,\n\t\t\t\t\t&status->buffer[buffer_pos], status->buffer_index - buffer_pos);\n\t\t\tenum json_tokener_error err = json_tokener_get_error(status->tokener);\n\t\t\tif (err == json_tokener_success) {\n\t\t\t\tif (json_object_get_type(test_object) == json_type_array) {\n\t\t\t\t\tif (last_object) {\n\t\t\t\t\t\tjson_object_put(last_object);\n\t\t\t\t\t}\n\t\t\t\t\tlast_object = test_object;\n\t\t\t\t} else {\n\t\t\t\t\tjson_object_put(test_object);\n\t\t\t\t}\n\n\t\t\t\t// in order to print the json for debugging purposes\n\t\t\t\t// the last character is temporarily replaced with a null character\n\t\t\t\t// (the last character is used in case the buffer is full)\n\t\t\t\tchar *last_char_pos =\n\t\t\t\t\t&status->buffer[buffer_pos + status->tokener->char_offset - 1];\n\t\t\t\tchar last_char = *last_char_pos;\n\t\t\t\twhile (isspace(last_char)) {\n\t\t\t\t\tlast_char = *--last_char_pos;\n\t\t\t\t}\n\t\t\t\t*last_char_pos = '\\0';\n\t\t\t\tsize_t offset = strspn(&status->buffer[buffer_pos], \" \\f\\n\\r\\t\\v\");\n\t\t\t\tsway_log(SWAY_DEBUG, \"Received i3bar json: '%s%c'\",\n\t\t\t\t\t\t&status->buffer[buffer_pos + offset], last_char);\n\t\t\t\t*last_char_pos = last_char;\n\n\t\t\t\tbuffer_pos += status->tokener->char_offset;\n\t\t\t\tstatus->expecting_comma = true;\n\n\t\t\t\tif (buffer_pos < status->buffer_index) {\n\t\t\t\t\tcontinue; // look for comma without reading more input\n\t\t\t\t}\n\t\t\t\tbuffer_pos = status->buffer_index = 0;\n\t\t\t} else if (err == json_tokener_continue) {\n\t\t\t\tjson_tokener_reset(status->tokener);\n\t\t\t\tif (status->buffer_index < status->buffer_size) {\n\t\t\t\t\t// move the object to the start of the buffer\n\t\t\t\t\tstatus->buffer_index -= buffer_pos;\n\t\t\t\t\tmemmove(status->buffer, &status->buffer[buffer_pos],\n\t\t\t\t\t\t\tstatus->buffer_index);\n\t\t\t\t\tbuffer_pos = 0;\n\t\t\t\t} else {\n\t\t\t\t\t// expand buffer\n\t\t\t\t\tstatus->buffer_size *= 2;\n\t\t\t\t\tchar *new_buffer = realloc(status->buffer, status->buffer_size);\n\t\t\t\t\tif (new_buffer) {\n\t\t\t\t\t\tstatus->buffer = new_buffer;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfree(status->buffer);\n\t\t\t\t\t\tstatus_error(status, \"[failed to allocate buffer]\");\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tchar last_char = status->buffer[status->buffer_index - 1];\n\t\t\t\tstatus->buffer[status->buffer_index - 1] = '\\0';\n\t\t\t\tsway_log(SWAY_DEBUG, \"Failed to parse i3bar json - %s: '%s%c'\",\n\t\t\t\t\t\tjson_tokener_error_desc(err), &status->buffer[buffer_pos], last_char);\n\t\t\t\tstatus_error(status, \"[failed to parse i3bar json]\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\terrno = 0;\n\t\tssize_t read_bytes = read(status->read_fd, &status->buffer[status->buffer_index],\n\t\t\t\tstatus->buffer_size - status->buffer_index);\n\t\tif (read_bytes > -1) {\n\t\t\tstatus->buffer_index += read_bytes;\n\t\t} else if (errno == EAGAIN) {\n\t\t\tbreak;\n\t\t} else {\n\t\t\tstatus_error(status, \"[error reading from status command]\");\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (last_object) {\n\t\tsway_log(SWAY_DEBUG, \"Rendering last received json\");\n\t\ti3bar_parse_json(status, last_object);\n\t\tjson_object_put(last_object);\n\t\treturn true;\n\t} else {\n\t\treturn false;\n\t}\n}\n\nenum hotspot_event_handling i3bar_block_send_click(struct status_line *status,\n\t\tstruct i3bar_block *block, double x, double y, double rx, double ry,\n\t\tdouble w, double h, int scale, uint32_t button, bool released) {\n\tsway_log(SWAY_DEBUG, \"block %s clicked\", block->name);\n\tif (!block->name || !status->click_events) {\n\t\treturn HOTSPOT_PROCESS;\n\t}\n\tif (released) {\n\t\t// Since we handle the pressed event, also handle the released event\n\t\t// to block it from falling through to a binding in the bar\n\t\treturn HOTSPOT_IGNORE;\n\t}\n\n\tstruct json_object *event_json = json_object_new_object();\n\tjson_object_object_add(event_json, \"name\",\n\t\t\tjson_object_new_string(block->name));\n\tif (block->instance) {\n\t\tjson_object_object_add(event_json, \"instance\",\n\t\t\t\tjson_object_new_string(block->instance));\n\t}\n\n\tjson_object_object_add(event_json, \"button\",\n\t\t\tjson_object_new_int(event_to_x11_button(button)));\n\tjson_object_object_add(event_json, \"event\", json_object_new_int(button));\n\tif (status->float_event_coords) {\n\t\tjson_object_object_add(event_json, \"x\", json_object_new_double(x));\n\t\tjson_object_object_add(event_json, \"y\", json_object_new_double(y));\n\t\tjson_object_object_add(event_json, \"relative_x\", json_object_new_double(rx));\n\t\tjson_object_object_add(event_json, \"relative_y\", json_object_new_double(ry));\n\t\tjson_object_object_add(event_json, \"width\", json_object_new_double(w));\n\t\tjson_object_object_add(event_json, \"height\", json_object_new_double(h));\n\t} else {\n\t\tjson_object_object_add(event_json, \"x\", json_object_new_int(x));\n\t\tjson_object_object_add(event_json, \"y\", json_object_new_int(y));\n\t\tjson_object_object_add(event_json, \"relative_x\", json_object_new_int(rx));\n\t\tjson_object_object_add(event_json, \"relative_y\", json_object_new_int(ry));\n\t\tjson_object_object_add(event_json, \"width\", json_object_new_int(w));\n\t\tjson_object_object_add(event_json, \"height\", json_object_new_int(h));\n\t}\n\tjson_object_object_add(event_json, \"scale\", json_object_new_int(scale));\n\tif (dprintf(status->write_fd, \"%s%s\\n\", status->clicked ? \",\" : \"\",\n\t\t\t\tjson_object_to_json_string(event_json)) < 0) {\n\t\tstatus_error(status, \"[failed to write click event]\");\n\t}\n\tstatus->clicked = true;\n\tjson_object_put(event_json);\n\treturn HOTSPOT_IGNORE;\n}\n"
  },
  {
    "path": "swaybar/image.c",
    "content": "#include <assert.h>\n#include \"config.h\"\n#include \"log.h\"\n#include \"swaybar/image.h\"\n\n#if HAVE_GDK_PIXBUF\n#include <gdk-pixbuf/gdk-pixbuf.h>\n#endif\n\n#if HAVE_GDK_PIXBUF\nstatic cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(\n\t\tconst GdkPixbuf *gdkbuf) {\n\tint chan = gdk_pixbuf_get_n_channels(gdkbuf);\n\tif (chan < 3) {\n\t\treturn NULL;\n\t}\n\n\tconst guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);\n\tif (!gdkpix) {\n\t\treturn NULL;\n\t}\n\tgint w = gdk_pixbuf_get_width(gdkbuf);\n\tgint h = gdk_pixbuf_get_height(gdkbuf);\n\tint stride = gdk_pixbuf_get_rowstride(gdkbuf);\n\n\tcairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;\n\tcairo_surface_t * cs = cairo_image_surface_create (fmt, w, h);\n\tcairo_surface_flush (cs);\n\tif ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) {\n\t\treturn NULL;\n\t}\n\n\tint cstride = cairo_image_surface_get_stride(cs);\n\tunsigned char * cpix = cairo_image_surface_get_data(cs);\n\n\tif (chan == 3) {\n\t\tint i;\n\t\tfor (i = h; i; --i) {\n\t\t\tconst guint8 *gp = gdkpix;\n\t\t\tunsigned char *cp = cpix;\n\t\t\tconst guint8* end = gp + 3*w;\n\t\t\twhile (gp < end) {\n#if G_BYTE_ORDER == G_LITTLE_ENDIAN\n\t\t\t\tcp[0] = gp[2];\n\t\t\t\tcp[1] = gp[1];\n\t\t\t\tcp[2] = gp[0];\n#else\n\t\t\t\tcp[1] = gp[0];\n\t\t\t\tcp[2] = gp[1];\n\t\t\t\tcp[3] = gp[2];\n#endif\n\t\t\t\tgp += 3;\n\t\t\t\tcp += 4;\n\t\t\t}\n\t\t\tgdkpix += stride;\n\t\t\tcpix += cstride;\n\t\t}\n\t} else {\n\t\t/* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255\n\t\t * (z/255) = z/256 * 256/255     = z/256 (1 + 1/255)\n\t\t *         = z/256 + (z/256)/255 = (z + z/255)/256\n\t\t *         # recurse once\n\t\t *         = (z + (z + z/255)/256)/256\n\t\t *         = (z + z/256 + z/256/255) / 256\n\t\t *         # only use 16bit uint operations, loose some precision,\n\t\t *         # result is floored.\n\t\t *       ->  (z + z>>8)>>8\n\t\t *         # add 0x80/255 = 0.5 to convert floor to round\n\t\t *       =>  (z+0x80 + (z+0x80)>>8 ) >> 8\n\t\t * ------\n\t\t * tested as equal to lround(z/255.0) for uint z in [0..0xfe02]\n\t\t */\n#define PREMUL_ALPHA(x,a,b,z) \\\n\t\tG_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \\\n\t\tG_STMT_END\n\t\tint i;\n\t\tfor (i = h; i; --i) {\n\t\t\tconst guint8 *gp = gdkpix;\n\t\t\tunsigned char *cp = cpix;\n\t\t\tconst guint8* end = gp + 4*w;\n\t\t\tguint z1, z2, z3;\n\t\t\twhile (gp < end) {\n#if G_BYTE_ORDER == G_LITTLE_ENDIAN\n\t\t\t\tPREMUL_ALPHA(cp[0], gp[2], gp[3], z1);\n\t\t\t\tPREMUL_ALPHA(cp[1], gp[1], gp[3], z2);\n\t\t\t\tPREMUL_ALPHA(cp[2], gp[0], gp[3], z3);\n\t\t\t\tcp[3] = gp[3];\n#else\n\t\t\t\tPREMUL_ALPHA(cp[1], gp[0], gp[3], z1);\n\t\t\t\tPREMUL_ALPHA(cp[2], gp[1], gp[3], z2);\n\t\t\t\tPREMUL_ALPHA(cp[3], gp[2], gp[3], z3);\n\t\t\t\tcp[0] = gp[3];\n#endif\n\t\t\t\tgp += 4;\n\t\t\t\tcp += 4;\n\t\t\t}\n\t\t\tgdkpix += stride;\n\t\t\tcpix += cstride;\n\t\t}\n#undef PREMUL_ALPHA\n\t}\n\tcairo_surface_mark_dirty(cs);\n\treturn cs;\n}\n#endif // HAVE_GDK_PIXBUF\n\ncairo_surface_t *load_image(const char *path) {\n\tcairo_surface_t *image;\n#if HAVE_GDK_PIXBUF\n\tGError *err = NULL;\n\tGdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);\n\tif (!pixbuf) {\n\t\tsway_log(SWAY_ERROR, \"Failed to load background image (%s).\",\n\t\t\t\terr->message);\n\t\treturn NULL;\n\t}\n\timage = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);\n\tg_object_unref(pixbuf);\n#else\n\timage = cairo_image_surface_create_from_png(path);\n#endif // HAVE_GDK_PIXBUF\n\tif (!image) {\n\t\tsway_log(SWAY_ERROR, \"Failed to read background image.\");\n\t\treturn NULL;\n\t}\n\tif (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {\n\t\tsway_log(SWAY_ERROR, \"Failed to read background image: %s.\"\n#if !HAVE_GDK_PIXBUF\n\t\t\t\t\"\\nSway was compiled without gdk_pixbuf support, so only\"\n\t\t\t\t\"\\nPNG images can be loaded. This is the likely cause.\"\n#endif // !HAVE_GDK_PIXBUF\n\t\t\t\t, cairo_status_to_string(cairo_surface_status(image)));\n\t\treturn NULL;\n\t}\n\treturn image;\n}\n"
  },
  {
    "path": "swaybar/input.c",
    "content": "#include <assert.h>\n#include <linux/input-event-codes.h>\n#include <stdlib.h>\n#include <wayland-client.h>\n#include <wayland-cursor.h>\n#include \"list.h\"\n#include \"log.h\"\n#include \"swaybar/bar.h\"\n#include \"swaybar/config.h\"\n#include \"swaybar/input.h\"\n#include \"swaybar/ipc.h\"\n\nvoid free_hotspots(struct wl_list *list) {\n\tstruct swaybar_hotspot *hotspot, *tmp;\n\twl_list_for_each_safe(hotspot, tmp, list, link) {\n\t\twl_list_remove(&hotspot->link);\n\t\tif (hotspot->destroy) {\n\t\t\thotspot->destroy(hotspot->data);\n\t\t}\n\t\tfree(hotspot);\n\t}\n}\n\nuint32_t event_to_x11_button(uint32_t event) {\n\tswitch (event) {\n\tcase BTN_LEFT:\n\t\treturn 1;\n\tcase BTN_MIDDLE:\n\t\treturn 2;\n\tcase BTN_RIGHT:\n\t\treturn 3;\n\tcase SWAY_SCROLL_UP:\n\t\treturn 4;\n\tcase SWAY_SCROLL_DOWN:\n\t\treturn 5;\n\tcase SWAY_SCROLL_LEFT:\n\t\treturn 6;\n\tcase SWAY_SCROLL_RIGHT:\n\t\treturn 7;\n\tcase BTN_SIDE:\n\t\treturn 8;\n\tcase BTN_EXTRA:\n\t\treturn 9;\n\tdefault:\n\t\treturn 0;\n\t}\n}\n\nstatic uint32_t wl_axis_to_button(uint32_t axis, wl_fixed_t value) {\n\tbool negative = wl_fixed_to_double(value) < 0;\n\tswitch (axis) {\n\tcase WL_POINTER_AXIS_VERTICAL_SCROLL:\n\t\treturn negative ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;\n\tcase WL_POINTER_AXIS_HORIZONTAL_SCROLL:\n\t\treturn negative ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;\n\tdefault:\n\t\tsway_log(SWAY_DEBUG, \"Unexpected axis value on mouse scroll\");\n\t\treturn 0;\n\t}\n}\n\nvoid update_cursor(struct swaybar_seat *seat) {\n\tstruct swaybar_pointer *pointer = &seat->pointer;\n\tif (!pointer || !pointer->cursor_surface) {\n\t\treturn;\n\t}\n\tif (pointer->cursor_theme) {\n\t\twl_cursor_theme_destroy(pointer->cursor_theme);\n\t}\n\tconst char *cursor_theme = getenv(\"XCURSOR_THEME\");\n\tunsigned cursor_size = 24;\n\tconst char *env_cursor_size = getenv(\"XCURSOR_SIZE\");\n\tif (env_cursor_size && strlen(env_cursor_size) > 0) {\n\t\terrno = 0;\n\t\tchar *end;\n\t\tunsigned size = strtoul(env_cursor_size, &end, 10);\n\t\tif (!*end && errno == 0) {\n\t\t\tcursor_size = size;\n\t\t}\n\t}\n\tint scale = pointer->current ? pointer->current->scale : 1;\n\tpointer->cursor_theme = wl_cursor_theme_load(\n\t\tcursor_theme, cursor_size * scale, seat->bar->shm);\n\tif (!pointer->cursor_theme) {\n\t\tsway_log(SWAY_ERROR, \"Failed to load cursor theme\");\n\t\treturn;\n\t}\n\tstruct wl_cursor *cursor;\n\tcursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, \"default\");\n\tif (!cursor) {\n\t\tsway_log(SWAY_ERROR, \"Failed to get default cursor from theme\");\n\t\treturn;\n\t}\n\tpointer->cursor_image = cursor->images[0];\n\twl_surface_set_buffer_scale(pointer->cursor_surface, scale);\n\twl_surface_attach(pointer->cursor_surface,\n\t\t\twl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);\n\twl_pointer_set_cursor(pointer->pointer, pointer->serial,\n\t\t\tpointer->cursor_surface,\n\t\t\tpointer->cursor_image->hotspot_x / scale,\n\t\t\tpointer->cursor_image->hotspot_y / scale);\n\twl_surface_damage_buffer(pointer->cursor_surface, 0, 0,\n\t\t\tINT32_MAX, INT32_MAX);\n\twl_surface_commit(pointer->cursor_surface);\n}\n\nstatic void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t serial, struct wl_surface *surface,\n\t\twl_fixed_t surface_x, wl_fixed_t surface_y) {\n\tstruct swaybar_seat *seat = data;\n\tstruct swaybar_pointer *pointer = &seat->pointer;\n\tseat->pointer.x = wl_fixed_to_double(surface_x);\n\tseat->pointer.y = wl_fixed_to_double(surface_y);\n\n\tstruct swaybar_output *output;\n\twl_list_for_each(output, &seat->bar->outputs, link) {\n\t\tif (output->surface == surface) {\n\t\t\tpointer->current = output;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (seat->bar->cursor_shape_manager) {\n\t\tstruct wp_cursor_shape_device_v1 *device =\n\t\t\twp_cursor_shape_manager_v1_get_pointer(\n\t\t\t\tseat->bar->cursor_shape_manager, wl_pointer);\n\t\twp_cursor_shape_device_v1_set_shape(device, serial,\n\t\t\tWP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);\n\t\twp_cursor_shape_device_v1_destroy(device);\n\t} else {\n\t\tpointer->serial = serial;\n\t\tupdate_cursor(seat);\n\t}\n}\n\nstatic void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t serial, struct wl_surface *surface) {\n\tstruct swaybar_seat *seat = data;\n\tseat->pointer.current = NULL;\n}\n\nstatic void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {\n\tstruct swaybar_seat *seat = data;\n\tseat->pointer.x = wl_fixed_to_double(surface_x);\n\tseat->pointer.y = wl_fixed_to_double(surface_y);\n}\n\nstatic bool check_bindings(struct swaybar *bar, uint32_t button,\n\t\tuint32_t state) {\n\tbool released = state == WL_POINTER_BUTTON_STATE_RELEASED;\n\tfor (int i = 0; i < bar->config->bindings->length; i++) {\n\t\tstruct swaybar_binding *binding = bar->config->bindings->items[i];\n\t\tif (binding->button == button && binding->release == released) {\n\t\t\tipc_execute_binding(bar, binding);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic bool process_hotspots(struct swaybar_output *output,\n\t\tdouble x, double y, uint32_t button, uint32_t state) {\n\tbool released = state == WL_POINTER_BUTTON_STATE_RELEASED;\n\tstruct swaybar_hotspot *hotspot;\n\twl_list_for_each(hotspot, &output->hotspots, link) {\n\t\tif (x >= hotspot->x && y >= hotspot->y\n\t\t\t\t&& x < hotspot->x + hotspot->width\n\t\t\t\t&& y < hotspot->y + hotspot->height) {\n\t\t\tif (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,\n\t\t\t\t\tbutton, released, hotspot->data)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t serial, uint32_t time, uint32_t button, uint32_t state) {\n\tstruct swaybar_seat *seat = data;\n\tstruct swaybar_pointer *pointer = &seat->pointer;\n\tstruct swaybar_output *output = pointer->current;\n\tif (!sway_assert(output, \"button with no active output\")) {\n\t\treturn;\n\t}\n\n\tif (process_hotspots(output, pointer->x, pointer->y, button, state)) {\n\t\treturn;\n\t}\n\n\tcheck_bindings(seat->bar, button, state);\n}\n\nstatic void workspace_next(struct swaybar *bar, struct swaybar_output *output,\n\t\tbool left) {\n\tstruct swaybar_config *config = bar->config;\n\tstruct swaybar_workspace *first =\n\t\twl_container_of(output->workspaces.next, first, link);\n\tstruct swaybar_workspace *last =\n\t\twl_container_of(output->workspaces.prev, last, link);\n\n\tstruct swaybar_workspace *active;\n\twl_list_for_each(active, &output->workspaces, link) {\n\t\tif (active->visible) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!sway_assert(active->visible, \"axis with null workspace\")) {\n\t\treturn;\n\t}\n\n\tstruct swaybar_workspace *new;\n\tif (left) {\n\t\tif (active == first) {\n\t\t\tnew = config->wrap_scroll ? last : NULL;\n\t\t} else {\n\t\t\tnew = wl_container_of(active->link.prev, new, link);\n\t\t}\n\t} else {\n\t\tif (active == last) {\n\t\t\tnew = config->wrap_scroll ? first : NULL;\n\t\t} else {\n\t\t\tnew = wl_container_of(active->link.next, new, link);\n\t\t}\n\t}\n\n\tif (new && new != active) {\n\t\tipc_send_workspace_command(bar, new->name);\n\n\t\t// Since we're asking Sway to switch to 'new', it should become visible.\n\t\t// Marking it visible right now allows calling workspace_next in a loop.\n\t\tnew->visible = true;\n\t\tactive->visible = false;\n\t}\n}\n\nstatic void process_discrete_scroll(struct swaybar_seat *seat,\n\t\tstruct swaybar_output *output, struct swaybar_pointer *pointer,\n\t\tuint32_t axis, wl_fixed_t value) {\n\tuint32_t button = wl_axis_to_button(axis, value);\n\tif (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) {\n\t\t// (Currently hotspots don't do anything on release events, so no need to emit one)\n\t\treturn;\n\t}\n\n\t// If there is a button press binding, execute it, and check button release bindings\n\tif (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {\n\t\tcheck_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);\n\t\treturn;\n\t}\n\n\tstruct swaybar_config *config = seat->bar->config;\n\tdouble amt = wl_fixed_to_double(value);\n\tif (amt == 0.0 || !config->workspace_buttons) {\n\t\tcheck_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);\n\t\treturn;\n\t}\n\n\tif (!sway_assert(!wl_list_empty(&output->workspaces), \"axis with no workspaces\")) {\n\t\treturn;\n\t}\n\n\tworkspace_next(seat->bar, output, amt < 0.0);\n\n\t// Check button release bindings\n\tcheck_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);\n}\n\nstatic void process_continuous_scroll(struct swaybar_seat *seat,\n\t\tstruct swaybar_output *output, struct swaybar_pointer *pointer,\n\t\tuint32_t axis) {\n\twhile (abs(seat->axis[axis].value) > SWAY_CONTINUOUS_SCROLL_THRESHOLD) {\n\t\tif (seat->axis[axis].value > 0) {\n\t\t\tprocess_discrete_scroll(seat, output, pointer, axis,\n\t\t\t\tSWAY_CONTINUOUS_SCROLL_THRESHOLD);\n\t\t\tseat->axis[axis].value -= SWAY_CONTINUOUS_SCROLL_THRESHOLD;\n\t\t} else {\n\t\t\tprocess_discrete_scroll(seat, output, pointer, axis,\n\t\t\t\t-SWAY_CONTINUOUS_SCROLL_THRESHOLD);\n\t\t\tseat->axis[axis].value += SWAY_CONTINUOUS_SCROLL_THRESHOLD;\n\t\t}\n\t}\n}\n\nstatic void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t time, uint32_t axis, wl_fixed_t value) {\n\tstruct swaybar_seat *seat = data;\n\tstruct swaybar_pointer *pointer = &seat->pointer;\n\tstruct swaybar_output *output = pointer->current;\n\tif (!sway_assert(output, \"axis with no active output\")) {\n\t\treturn;\n\t}\n\n\tif (!sway_assert(axis < 2, \"axis out of range\")) {\n\t\treturn;\n\t}\n\n\t// If there's a while since the last scroll event,\n\t// set 'value' to zero as if to reset the \"virtual scroll wheel\"\n\tif (seat->axis[axis].discrete_steps == 0 &&\n\t\t\ttime - seat->axis[axis].update_time > SWAY_CONTINUOUS_SCROLL_TIMEOUT) {\n\t\tseat->axis[axis].value = 0;\n\t}\n\n\tseat->axis[axis].value += value;\n\tseat->axis[axis].update_time = time;\n}\n\nstatic void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {\n\tstruct swaybar_seat *seat = data;\n\tstruct swaybar_pointer *pointer = &seat->pointer;\n\tstruct swaybar_output *output = pointer->current;\n\n\tif (output == NULL) {\n\t\treturn;\n\t}\n\n\tfor (uint32_t axis = 0; axis < 2; ++axis) {\n\t\tif (seat->axis[axis].discrete_steps) {\n\t\t\tfor (uint32_t step = 0; step < seat->axis[axis].discrete_steps; ++step) {\n\t\t\t\t// Honestly, it would probabyl make sense to pass in\n\t\t\t\t// 'seat->axis[axis].value / seat->axis[axi].discrete_steps' here,\n\t\t\t\t// but it's only used to check whether it's positive or negative\n\t\t\t\t// so I don't think it's worth the risk of rounding errors.\n\t\t\t\tprocess_discrete_scroll(seat, output, pointer, axis,\n\t\t\t\t\tseat->axis[axis].value);\n\t\t\t}\n\n\t\t\tseat->axis[axis].value = 0;\n\t\t\tseat->axis[axis].discrete_steps = 0;\n\t\t} else {\n\t\t\tprocess_continuous_scroll(seat, output, pointer, axis);\n\t\t}\n\t}\n}\n\nstatic void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t axis_source) {\n\t// Who cares\n}\n\nstatic void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t time, uint32_t axis) {\n\t// Who cares\n}\n\nstatic void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t axis, int32_t discrete) {\n\tstruct swaybar_seat *seat = data;\n\tif (!sway_assert(axis < 2, \"axis out of range\")) {\n\t\treturn;\n\t}\n\n\tseat->axis[axis].discrete_steps += abs(discrete);\n}\n\nstatic const struct wl_pointer_listener pointer_listener = {\n\t.enter = wl_pointer_enter,\n\t.leave = wl_pointer_leave,\n\t.motion = wl_pointer_motion,\n\t.button = wl_pointer_button,\n\t.axis = wl_pointer_axis,\n\t.frame = wl_pointer_frame,\n\t.axis_source = wl_pointer_axis_source,\n\t.axis_stop = wl_pointer_axis_stop,\n\t.axis_discrete = wl_pointer_axis_discrete,\n};\n\nstatic struct touch_slot *get_touch_slot(struct swaybar_touch *touch, int32_t id) {\n\tssize_t next = -1;\n\tfor (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) {\n\t\tif (touch->slots[i].id == id) {\n\t\t\treturn &touch->slots[i];\n\t\t}\n\t\tif (next == -1 && !touch->slots[i].output) {\n\t\t\tnext = i;\n\t\t}\n\t}\n\tif (next == -1) {\n\t\tsway_log(SWAY_ERROR, \"Ran out of touch slots\");\n\t\treturn NULL;\n\t}\n\treturn &touch->slots[next];\n}\n\nstatic void wl_touch_down(void *data, struct wl_touch *wl_touch,\n\t\tuint32_t serial, uint32_t time, struct wl_surface *surface,\n\t\tint32_t id, wl_fixed_t _x, wl_fixed_t _y) {\n\tstruct swaybar_seat *seat = data;\n\tstruct swaybar_output *_output = NULL, *output = NULL;\n\twl_list_for_each(_output, &seat->bar->outputs, link) {\n\t\tif (_output->surface == surface) {\n\t\t\toutput = _output;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!output) {\n\t\tsway_log(SWAY_DEBUG, \"Got touch event for unknown surface\");\n\t\treturn;\n\t}\n\tstruct touch_slot *slot = get_touch_slot(&seat->touch, id);\n\tif (!slot) {\n\t\treturn;\n\t}\n\tslot->id = id;\n\tslot->output = output;\n\tslot->x = slot->start_x = wl_fixed_to_double(_x);\n\tslot->y = slot->start_y = wl_fixed_to_double(_y);\n\tslot->time = time;\n}\n\nstatic void wl_touch_up(void *data, struct wl_touch *wl_touch,\n\t\tuint32_t serial, uint32_t time, int32_t id) {\n\tstruct swaybar_seat *seat = data;\n\tstruct touch_slot *slot = get_touch_slot(&seat->touch, id);\n\tif (!slot) {\n\t\treturn;\n\t}\n\tif (time - slot->time < 500) {\n\t\t// Tap, treat it like a pointer click\n\t\tprocess_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);\n\t\t// (Currently hotspots don't do anything on release events, so no need to emit one)\n\t}\n\tslot->output = NULL;\n}\n\nstatic void wl_touch_motion(void *data, struct wl_touch *wl_touch,\n\t\tuint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) {\n\tstruct swaybar_seat *seat = data;\n\tstruct touch_slot *slot = get_touch_slot(&seat->touch, id);\n\tif (!slot) {\n\t\treturn;\n\t}\n\tint prev_progress = (int)((slot->x - slot->start_x)\n\t\t\t/ slot->output->width * 100);\n\tslot->x = wl_fixed_to_double(x);\n\tslot->y = wl_fixed_to_double(y);\n\t// \"progress\" is a measure from 0..100 representing the fraction of the\n\t// output the touch gesture has travelled, positive when moving to the right\n\t// and negative when moving to the left.\n\tint progress = (int)((slot->x - slot->start_x)\n\t\t\t/ slot->output->width * 100);\n\tif (abs(progress) / 20 != abs(prev_progress) / 20) {\n\t\tworkspace_next(seat->bar, slot->output, progress - prev_progress < 0);\n\t}\n}\n\nstatic void wl_touch_frame(void *data, struct wl_touch *wl_touch) {\n\t// Who cares\n}\n\nstatic void wl_touch_cancel(void *data, struct wl_touch *wl_touch) {\n\tstruct swaybar_seat *seat = data;\n\tstruct swaybar_touch *touch = &seat->touch;\n\tfor (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) {\n\t\ttouch->slots[i].output = NULL;\n\t}\n}\n\nstatic void wl_touch_shape(void *data, struct wl_touch *wl_touch, int32_t id,\n\t\twl_fixed_t major, wl_fixed_t minor) {\n\t// Who cares\n}\n\nstatic void wl_touch_orientation(void *data, struct wl_touch *wl_touch,\n\t\tint32_t id, wl_fixed_t orientation) {\n\t// Who cares\n}\n\nstatic const struct wl_touch_listener touch_listener = {\n\t.down = wl_touch_down,\n\t.up = wl_touch_up,\n\t.motion = wl_touch_motion,\n\t.frame = wl_touch_frame,\n\t.cancel = wl_touch_cancel,\n\t.shape = wl_touch_shape,\n\t.orientation = wl_touch_orientation,\n};\n\nstatic void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,\n\t\tenum wl_seat_capability caps) {\n\tstruct swaybar_seat *seat = data;\n\n\tbool have_pointer = caps & WL_SEAT_CAPABILITY_POINTER;\n\tbool have_touch = caps & WL_SEAT_CAPABILITY_TOUCH;\n\n\tif (!have_pointer && seat->pointer.pointer != NULL) {\n\t\twl_pointer_release(seat->pointer.pointer);\n\t\tseat->pointer.pointer = NULL;\n\t} else if (have_pointer && seat->pointer.pointer == NULL) {\n\t\tseat->pointer.pointer = wl_seat_get_pointer(wl_seat);\n\t\tif (seat->bar->running && !seat->pointer.cursor_surface) {\n\t\t\tseat->pointer.cursor_surface =\n\t\t\t\twl_compositor_create_surface(seat->bar->compositor);\n\t\t\tassert(seat->pointer.cursor_surface);\n\t\t}\n\t\twl_pointer_add_listener(seat->pointer.pointer, &pointer_listener, seat);\n\t}\n\tif (!have_touch && seat->touch.touch != NULL) {\n\t\twl_touch_release(seat->touch.touch);\n\t\tseat->touch.touch = NULL;\n\t} else if (have_touch && seat->touch.touch == NULL) {\n\t\tseat->touch.touch = wl_seat_get_touch(wl_seat);\n\t\twl_touch_add_listener(seat->touch.touch, &touch_listener, seat);\n\t}\n}\n\nstatic void seat_handle_name(void *data, struct wl_seat *wl_seat,\n\t\tconst char *name) {\n\t// Who cares\n}\n\nconst struct wl_seat_listener seat_listener = {\n\t.capabilities = seat_handle_capabilities,\n\t.name = seat_handle_name,\n};\n\nvoid swaybar_seat_free(struct swaybar_seat *seat) {\n\tif (!seat) {\n\t\treturn;\n\t}\n\tif (seat->pointer.pointer != NULL) {\n\t\twl_pointer_release(seat->pointer.pointer);\n\t}\n\tif (seat->pointer.cursor_theme != NULL) {\n\t\twl_cursor_theme_destroy(seat->pointer.cursor_theme);\n\t}\n\tif (seat->pointer.cursor_surface != NULL) {\n\t\twl_surface_destroy(seat->pointer.cursor_surface);\n\t}\n\tif (seat->touch.touch != NULL) {\n\t\twl_touch_release(seat->touch.touch);\n\t}\n\twl_seat_destroy(seat->wl_seat);\n\twl_list_remove(&seat->link);\n\tfree(seat);\n}\n"
  },
  {
    "path": "swaybar/ipc.c",
    "content": "#include <limits.h>\n#include <poll.h>\n#include <stdio.h>\n#include <string.h>\n#include <strings.h>\n#include <json.h>\n#include \"swaybar/config.h\"\n#include \"swaybar/ipc.h\"\n#include \"swaybar/status_line.h\"\n#if HAVE_TRAY\n#include \"swaybar/tray/tray.h\"\n#endif\n#include \"config.h\"\n#include \"ipc-client.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"loop.h\"\n#include \"stringop.h\"\n#include \"util.h\"\n\nvoid ipc_send_workspace_command(struct swaybar *bar, const char *ws) {\n\tuint32_t size = strlen(\"workspace \\\"\\\"\") + strlen(ws);\n\tfor (size_t i = 0; i < strlen(ws); ++i) {\n\t\tif (ws[i] == '\"' || ws[i] == '\\\\') {\n\t\t\t++size;\n\t\t}\n\t}\n\n\tchar *command = malloc(size + 1);\n\tif (!command) {\n\t\treturn;\n\t}\n\n\tstrcpy(command, \"workspace \\\"\");\n\tstrcpy(&command[size - 1], \"\\\"\");\n\tfor (size_t i = 0, d = strlen(\"workspace \\\"\"); i < strlen(ws); ++i) {\n\t\tif (ws[i] == '\"' || ws[i] == '\\\\') {\n\t\t\tcommand[d++] = '\\\\';\n\t\t}\n\t\tcommand[d++] = ws[i];\n\t}\n\n\tfree(ipc_single_command(bar->ipc_socketfd, IPC_COMMAND, command, &size));\n\tfree(command);\n}\n\nchar *parse_font(const char *font) {\n\tchar *new_font = NULL;\n\tif (has_prefix(font, \"pango:\")) {\n\t\tfont += strlen(\"pango:\");\n\t}\n\tnew_font = strdup(font);\n\treturn new_font;\n}\n\nstatic void ipc_parse_colors(\n\t\tstruct swaybar_config *config, json_object *colors) {\n\tstruct {\n\t\tconst char *name;\n\t\tuint32_t *color;\n\t} properties[] = {\n\t\t{ \"background\", &config->colors.background },\n\t\t{ \"statusline\", &config->colors.statusline },\n\t\t{ \"separator\", &config->colors.separator },\n\t\t{ \"focused_background\", &config->colors.focused_background },\n\t\t{ \"focused_statusline\", &config->colors.focused_statusline },\n\t\t{ \"focused_separator\", &config->colors.focused_separator },\n\t\t{ \"focused_workspace_border\", &config->colors.focused_workspace.border },\n\t\t{ \"focused_workspace_bg\", &config->colors.focused_workspace.background },\n\t\t{ \"focused_workspace_text\", &config->colors.focused_workspace.text },\n\t\t{ \"active_workspace_border\", &config->colors.active_workspace.border },\n\t\t{ \"active_workspace_bg\", &config->colors.active_workspace.background },\n\t\t{ \"active_workspace_text\", &config->colors.active_workspace.text },\n\t\t{ \"inactive_workspace_border\", &config->colors.inactive_workspace.border },\n\t\t{ \"inactive_workspace_bg\", &config->colors.inactive_workspace.background },\n\t\t{ \"inactive_workspace_text\", &config->colors.inactive_workspace.text },\n\t\t{ \"urgent_workspace_border\", &config->colors.urgent_workspace.border },\n\t\t{ \"urgent_workspace_bg\", &config->colors.urgent_workspace.background },\n\t\t{ \"urgent_workspace_text\", &config->colors.urgent_workspace.text },\n\t\t{ \"binding_mode_border\", &config->colors.binding_mode.border },\n\t\t{ \"binding_mode_bg\", &config->colors.binding_mode.background },\n\t\t{ \"binding_mode_text\", &config->colors.binding_mode.text },\n\t};\n\n\tfor (size_t i = 0; i < sizeof(properties) / sizeof(properties[i]); i++) {\n\t\tjson_object *object;\n\t\tif (json_object_object_get_ex(colors, properties[i].name, &object)) {\n\t\t\tconst char *hexstring = json_object_get_string(object);\n\t\t\tif (!parse_color(hexstring, properties[i].color)) {\n\t\t\t\tsway_log(SWAY_ERROR, \"Ignoring invalid %s: %s\",\n\t\t\t\t\t\tproperties[i].name, hexstring);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic bool ipc_parse_config(\n\t\tstruct swaybar_config *config, const char *payload) {\n\tjson_object *bar_config = json_tokener_parse(payload);\n\tjson_object *success;\n\tif (json_object_object_get_ex(bar_config, \"success\", &success)\n\t\t\t&& !json_object_get_boolean(success)) {\n\t\tsway_log(SWAY_ERROR, \"No bar with that ID. Use 'swaymsg -t \"\n\t\t\t\t\"get_bar_config' to get the available bar configs.\");\n\t\tjson_object_put(bar_config);\n\t\treturn false;\n\t}\n\n\tjson_object *bar_height = json_object_object_get(bar_config, \"bar_height\");\n\tif (bar_height) {\n\t\tconfig->height = json_object_get_int(bar_height);\n\t}\n\n\tjson_object *binding_mode_indicator =\n\t\tjson_object_object_get(bar_config, \"binding_mode_indicator\");\n\tif (binding_mode_indicator) {\n\t\tconfig->binding_mode_indicator =\n\t\t\tjson_object_get_boolean(binding_mode_indicator);\n\t}\n\n\tjson_object *bindings = json_object_object_get(bar_config, \"bindings\");\n\twhile (config->bindings->length) {\n\t\tstruct swaybar_binding *binding = config->bindings->items[0];\n\t\tlist_del(config->bindings, 0);\n\t\tfree_binding(binding);\n\t}\n\tif (bindings) {\n\t\tint length = json_object_array_length(bindings);\n\t\tfor (int i = 0; i < length; ++i) {\n\t\t\tjson_object *bindobj = json_object_array_get_idx(bindings, i);\n\t\t\tstruct swaybar_binding *binding =\n\t\t\t\tcalloc(1, sizeof(struct swaybar_binding));\n\t\t\tbinding->button = json_object_get_int(\n\t\t\t\t\tjson_object_object_get(bindobj, \"event_code\"));\n\t\t\tbinding->command = strdup(json_object_get_string(\n\t\t\t\t\tjson_object_object_get(bindobj, \"command\")));\n\t\t\tbinding->release = json_object_get_boolean(\n\t\t\t\t\tjson_object_object_get(bindobj, \"release\"));\n\t\t\tlist_add(config->bindings, binding);\n\t\t}\n\t}\n\n\tjson_object *colors = json_object_object_get(bar_config, \"colors\");\n\tif (colors) {\n\t\tipc_parse_colors(config, colors);\n\t}\n\n\tjson_object *font = json_object_object_get(bar_config, \"font\");\n\tif (font) {\n\t\tpango_font_description_free(config->font_description);\n\t\tchar *font_value = parse_font(json_object_get_string(font));\n\t\tconfig->font_description = pango_font_description_from_string(font_value);\n\t\tfree(font_value);\n\t}\n\n\tjson_object *gaps = json_object_object_get(bar_config, \"gaps\");\n\tif (gaps) {\n\t\tjson_object *top = json_object_object_get(gaps, \"top\");\n\t\tif (top) {\n\t\t\tconfig->gaps.top = json_object_get_int(top);\n\t\t}\n\t\tjson_object *right = json_object_object_get(gaps, \"right\");\n\t\tif (right) {\n\t\t\tconfig->gaps.right = json_object_get_int(right);\n\t\t}\n\t\tjson_object *bottom = json_object_object_get(gaps, \"bottom\");\n\t\tif (bottom) {\n\t\t\tconfig->gaps.bottom = json_object_get_int(bottom);\n\t\t}\n\t\tjson_object *left = json_object_object_get(gaps, \"left\");\n\t\tif (left) {\n\t\t\tconfig->gaps.left = json_object_get_int(left);\n\t\t}\n\t}\n\n\tjson_object *hidden_state =\n\t\tjson_object_object_get(bar_config, \"hidden_state\");\n\tif (hidden_state) {\n\t\tfree(config->hidden_state);\n\t\tconfig->hidden_state = strdup(json_object_get_string(hidden_state));\n\t}\n\n\tjson_object *mode = json_object_object_get(bar_config, \"mode\");\n\tif (mode) {\n\t\tfree(config->mode);\n\t\tconfig->mode = strdup(json_object_get_string(mode));\n\t}\n\n\tjson_object *outputs = json_object_object_get(bar_config, \"outputs\");\n\tstruct config_output *output, *tmp;\n\twl_list_for_each_safe(output, tmp, &config->outputs, link) {\n\t\twl_list_remove(&output->link);\n\t\tfree(output->name);\n\t\tfree(output);\n\t}\n\tif (outputs) {\n\t\tint length = json_object_array_length(outputs);\n\t\tfor (int i = 0; i < length; ++i) {\n\t\t\tjson_object *output = json_object_array_get_idx(outputs, i);\n\t\t\tconst char *name = json_object_get_string(output);\n\t\t\tif (strcmp(\"*\", name) == 0) {\n\t\t\t\tstruct config_output *coutput, *tmp;\n\t\t\t\twl_list_for_each_safe(coutput, tmp, &config->outputs, link) {\n\t\t\t\t\twl_list_remove(&coutput->link);\n\t\t\t\t\tfree(coutput->name);\n\t\t\t\t\tfree(coutput);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tstruct config_output *coutput = calloc(\n\t\t\t\t\t1, sizeof(struct config_output));\n\t\t\tcoutput->name = strdup(name);\n\t\t\twl_list_insert(&config->outputs, &coutput->link);\n\t\t}\n\t}\n\n\tjson_object *pango_markup =\n\t\tjson_object_object_get(bar_config, \"pango_markup\");\n\tif (pango_markup) {\n\t\tconfig->pango_markup = json_object_get_boolean(pango_markup);\n\t}\n\n\tjson_object *position = json_object_object_get(bar_config, \"position\");\n\tif (position) {\n\t\tconfig->position = parse_position(json_object_get_string(position));\n\t}\n\n\tjson_object *separator_symbol =\n\t\tjson_object_object_get(bar_config, \"separator_symbol\");\n\tif (separator_symbol) {\n\t\tfree(config->sep_symbol);\n\t\tconfig->sep_symbol = strdup(json_object_get_string(separator_symbol));\n\t}\n\n\tjson_object *status_command =\n\t\tjson_object_object_get(bar_config, \"status_command\");\n\tif (status_command) {\n\t\tconst char *command = json_object_get_string(status_command);\n\t\tfree(config->status_command);\n\t\tconfig->status_command = strdup(command);\n\t}\n\n\tjson_object *status_edge_padding =\n\t\tjson_object_object_get(bar_config, \"status_edge_padding\");\n\tif (status_edge_padding) {\n\t\tconfig->status_edge_padding = json_object_get_int(status_edge_padding);\n\t}\n\n\tjson_object *status_padding =\n\t\tjson_object_object_get(bar_config, \"status_padding\");\n\tif (status_padding) {\n\t\tconfig->status_padding = json_object_get_int(status_padding);\n\t}\n\n\tjson_object *strip_workspace_name =\n\t\tjson_object_object_get(bar_config, \"strip_workspace_name\");\n\tif (strip_workspace_name) {\n\t\tconfig->strip_workspace_name =\n\t\t\tjson_object_get_boolean(strip_workspace_name);\n\t}\n\n\tjson_object *strip_workspace_numbers =\n\t\tjson_object_object_get(bar_config, \"strip_workspace_numbers\");\n\tif (strip_workspace_numbers) {\n\t\tconfig->strip_workspace_numbers =\n\t\t\tjson_object_get_boolean(strip_workspace_numbers);\n\t}\n\n\tjson_object *workspace_buttons =\n\t\tjson_object_object_get(bar_config, \"workspace_buttons\");\n\tif (workspace_buttons) {\n\t\tconfig->workspace_buttons = json_object_get_boolean(workspace_buttons);\n\t}\n\n\tjson_object *workspace_min_width =\n\t\tjson_object_object_get(bar_config, \"workspace_min_width\");\n\tif (workspace_min_width) {\n\t\tconfig->workspace_min_width = json_object_get_int(workspace_min_width);\n\t}\n\n\tjson_object *wrap_scroll = json_object_object_get(bar_config, \"wrap_scroll\");\n\tif (wrap_scroll) {\n\t\tconfig->wrap_scroll = json_object_get_boolean(wrap_scroll);\n\t}\n#if HAVE_TRAY\n\tjson_object *tray_outputs, *tray_padding, *tray_bindings, *icon_theme;\n\n\tif (config->tray_outputs && config->tray_outputs->length) {\n\t\tlist_free_items_and_destroy(config->tray_outputs);\n\t}\n\tif ((json_object_object_get_ex(bar_config, \"tray_outputs\", &tray_outputs))) {\n\t\tconfig->tray_outputs = create_list();\n\t\tint length = json_object_array_length(tray_outputs);\n\t\tfor (int i = 0; i < length; ++i) {\n\t\t\tjson_object *output= json_object_array_get_idx(tray_outputs, i);\n\t\t\tconst char *name = json_object_get_string(output);\n\t\t\tif (strcmp(name, \"none\") == 0) {\n\t\t\t\tconfig->tray_hidden = true;\n\t\t\t\tlist_free_items_and_destroy(config->tray_outputs);\n\t\t\t\tconfig->tray_outputs = create_list();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlist_add(config->tray_outputs, strdup(name));\n\t\t}\n\t}\n\n\tif ((json_object_object_get_ex(bar_config, \"tray_padding\", &tray_padding))) {\n\t\tconfig->tray_padding = json_object_get_int(tray_padding);\n\t}\n\n\tstruct tray_binding *tray_bind = NULL, *tmp_tray_bind = NULL;\n\twl_list_for_each_safe(tray_bind, tmp_tray_bind, &config->tray_bindings,\n\t\t\tlink) {\n\t\twl_list_remove(&tray_bind->link);\n\t\tfree_tray_binding(tray_bind);\n\t}\n\tif ((json_object_object_get_ex(bar_config, \"tray_bindings\", &tray_bindings))) {\n\t\tint length = json_object_array_length(tray_bindings);\n\t\tfor (int i = 0; i < length; ++i) {\n\t\t\tjson_object *bind = json_object_array_get_idx(tray_bindings, i);\n\t\t\tstruct tray_binding *binding =\n\t\t\t\tcalloc(1, sizeof(struct tray_binding));\n\t\t\tbinding->button = json_object_get_int(\n\t\t\t\t\tjson_object_object_get(bind, \"event_code\"));\n\t\t\tbinding->command = strdup(json_object_get_string(\n\t\t\t\t\tjson_object_object_get(bind, \"command\")));\n\t\t\twl_list_insert(&config->tray_bindings, &binding->link);\n\t\t}\n\t}\n\n\tif ((json_object_object_get_ex(bar_config, \"icon_theme\", &icon_theme))) {\n\t\tconfig->icon_theme = strdup(json_object_get_string(icon_theme));\n\t}\n#endif\n\n\tjson_object_put(bar_config);\n\treturn true;\n}\n\nbool ipc_get_workspaces(struct swaybar *bar) {\n\tstruct swaybar_output *output;\n\twl_list_for_each(output, &bar->outputs, link) {\n\t\tfree_workspaces(&output->workspaces);\n\t\toutput->focused = false;\n\t}\n\tuint32_t len = 0;\n\tchar *res = ipc_single_command(bar->ipc_socketfd,\n\t\t\tIPC_GET_WORKSPACES, NULL, &len);\n\tjson_object *results = json_tokener_parse(res);\n\tif (!results) {\n\t\tfree(res);\n\t\treturn false;\n\t}\n\n\tbar->visible_by_urgency = false;\n\tsize_t length = json_object_array_length(results);\n\tjson_object *ws_json;\n\tjson_object *num, *name, *visible, *focused, *out, *urgent;\n\tfor (size_t i = 0; i < length; ++i) {\n\t\tws_json = json_object_array_get_idx(results, i);\n\n\t\tjson_object_object_get_ex(ws_json, \"num\", &num);\n\t\tjson_object_object_get_ex(ws_json, \"name\", &name);\n\t\tjson_object_object_get_ex(ws_json, \"visible\", &visible);\n\t\tjson_object_object_get_ex(ws_json, \"focused\", &focused);\n\t\tjson_object_object_get_ex(ws_json, \"output\", &out);\n\t\tjson_object_object_get_ex(ws_json, \"urgent\", &urgent);\n\n\t\twl_list_for_each(output, &bar->outputs, link) {\n\t\t\tconst char *ws_output = json_object_get_string(out);\n\t\t\tif (ws_output != NULL && strcmp(ws_output, output->name) == 0) {\n\t\t\t\tstruct swaybar_workspace *ws =\n\t\t\t\t\tcalloc(1, sizeof(struct swaybar_workspace));\n\t\t\t\tws->num = json_object_get_int(num);\n\t\t\t\tws->name = strdup(json_object_get_string(name));\n\t\t\t\tws->label = strdup(ws->name);\n\t\t\t\t// ws->num will be -1 if workspace name doesn't begin with int.\n\t\t\t\tif (ws->num != -1) {\n\t\t\t\t\tsize_t len_offset = snprintf(NULL, 0, \"%d\", ws->num);\n\t\t\t\t\tif (bar->config->strip_workspace_name) {\n\t\t\t\t\t\tfree(ws->label);\n\t\t\t\t\t\tws->label = malloc(len_offset + 1);\n\t\t\t\t\t\tsnprintf(ws->label, len_offset + 1, \"%d\", ws->num);\n\t\t\t\t\t} else if (bar->config->strip_workspace_numbers) {\n\t\t\t\t\t\tlen_offset += ws->label[len_offset] == ':';\n\t\t\t\t\t\tif (ws->name[len_offset] != '\\0') {\n\t\t\t\t\t\t\tfree(ws->label);\n\t\t\t\t\t\t\t// Strip number prefix [1-?:] using len_offset.\n\t\t\t\t\t\t\tws->label = strdup(ws->name + len_offset);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tws->visible = json_object_get_boolean(visible);\n\t\t\t\tws->focused = json_object_get_boolean(focused);\n\t\t\t\tif (ws->focused) {\n\t\t\t\t\toutput->focused = true;\n\t\t\t\t}\n\t\t\t\tws->urgent = json_object_get_boolean(urgent);\n\t\t\t\tif (ws->urgent) {\n\t\t\t\t\tbar->visible_by_urgency = true;\n\t\t\t\t}\n\t\t\t\twl_list_insert(output->workspaces.prev, &ws->link);\n\t\t\t}\n\t\t}\n\t}\n\tjson_object_put(results);\n\tfree(res);\n\treturn determine_bar_visibility(bar, false);\n}\n\nvoid ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) {\n\tsway_log(SWAY_DEBUG, \"Executing binding for button %u (release=%d): `%s`\",\n\t\t\tbind->button, bind->release, bind->command);\n\tuint32_t len = strlen(bind->command);\n\tfree(ipc_single_command(bar->ipc_socketfd,\n\t\t\tIPC_COMMAND, bind->command, &len));\n}\n\nbool ipc_initialize(struct swaybar *bar) {\n\tuint32_t len = strlen(bar->id);\n\tchar *res = ipc_single_command(bar->ipc_socketfd,\n\t\t\tIPC_GET_BAR_CONFIG, bar->id, &len);\n\tif (!ipc_parse_config(bar->config, res)) {\n\t\tfree(res);\n\t\treturn false;\n\t}\n\tfree(res);\n\n\tchar *subscribe =\n\t\t\"[ \\\"barconfig_update\\\", \\\"bar_state_update\\\", \\\"mode\\\", \\\"workspace\\\" ]\";\n\tlen = strlen(subscribe);\n\tfree(ipc_single_command(bar->ipc_event_socketfd,\n\t\t\tIPC_SUBSCRIBE, subscribe, &len));\n\treturn true;\n}\n\nstatic bool handle_bar_state_update(struct swaybar *bar, json_object *event) {\n\tjson_object *json_id;\n\tjson_object_object_get_ex(event, \"id\", &json_id);\n\tconst char *id = json_object_get_string(json_id);\n\tif (strcmp(id, bar->id) != 0) {\n\t\treturn false;\n\t}\n\n\tjson_object *visible_by_modifier;\n\tjson_object_object_get_ex(event, \"visible_by_modifier\", &visible_by_modifier);\n\tbar->visible_by_modifier = json_object_get_boolean(visible_by_modifier);\n\tif (bar->visible_by_modifier) {\n\t\t// If the bar is visible by modifier, clear both visible by mode and\n\t\t// urgency as modifier has precedence and the bar should be hidden\n\t\t// again when it is no longer visible by modifier.\n\t\tbar->visible_by_mode = false;\n\t\tbar->visible_by_urgency = false;\n\t}\n\treturn determine_bar_visibility(bar, false);\n}\n\nstatic bool handle_barconfig_update(struct swaybar *bar, const char *payload,\n\t\tjson_object *json_config) {\n\tjson_object *json_id = json_object_object_get(json_config, \"id\");\n\tconst char *id = json_object_get_string(json_id);\n\tif (strcmp(id, bar->id) != 0) {\n\t\treturn false;\n\t}\n\n\tstruct swaybar_config *newcfg = init_config();\n\tipc_parse_config(newcfg, payload);\n\n\tstruct swaybar_config *oldcfg = bar->config;\n\tbar->config = newcfg;\n\n\tstruct swaybar_output *output, *tmp_output;\n\twl_list_for_each_safe(output, tmp_output, &bar->outputs, link) {\n\t\tbool found = wl_list_empty(&newcfg->outputs);\n\t\tstruct config_output *coutput;\n\t\twl_list_for_each(coutput, &newcfg->outputs, link) {\n\t\t\tif (strcmp(coutput->name, output->name) == 0 ||\n\t\t\t\t\tstrcmp(coutput->name, output->identifier) == 0) {\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!found) {\n\t\t\tdestroy_layer_surface(output);\n\t\t\twl_list_remove(&output->link);\n\t\t\twl_list_insert(&bar->unused_outputs, &output->link);\n\t\t} else if (!pango_font_description_equal(oldcfg->font_description, newcfg->font_description)) {\n\t\t\toutput->height = 0;  // force update height\n\t\t}\n\t}\n\twl_list_for_each_safe(output, tmp_output, &bar->unused_outputs, link) {\n\t\tbool found = wl_list_empty(&newcfg->outputs);\n\t\tstruct config_output *coutput;\n\t\twl_list_for_each(coutput, &newcfg->outputs, link) {\n\t\t\tif (strcmp(coutput->name, output->name) == 0 ||\n\t\t\t\t\tstrcmp(coutput->name, output->identifier) == 0) {\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (found) {\n\t\t\twl_list_remove(&output->link);\n\t\t\twl_list_insert(&bar->outputs, &output->link);\n\t\t}\n\t}\n\n\tif (bar->status && (!newcfg->status_command ||\n\t\t\t\tstrcmp(newcfg->status_command, oldcfg->status_command) != 0)) {\n\t\tstatus_line_free(bar->status);\n\t\tbar->status = NULL;\n\t}\n\tif (!bar->status && newcfg->status_command) {\n\t\tbar->status = status_line_init(newcfg->status_command);\n\t\tbar->status->bar = bar;\n\t\tloop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN,\n\t\t\t\tstatus_in, bar);\n\t}\n\n#if HAVE_TRAY\n\tif (oldcfg->tray_hidden && !newcfg->tray_hidden) {\n\t\tbar->tray = create_tray(bar);\n\t\tloop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar);\n\t} else if (bar->tray && newcfg->tray_hidden) {\n\t\tloop_remove_fd(bar->eventloop, bar->tray->fd);\n\t\tdestroy_tray(bar->tray);\n\t\tbar->tray = NULL;\n\t}\n#endif\n\n\tif (newcfg->workspace_buttons) {\n\t\tipc_get_workspaces(bar);\n\t}\n\n\tbool moving_layer = strcmp(oldcfg->mode, newcfg->mode) != 0;\n\n\tfree_config(oldcfg);\n\tdetermine_bar_visibility(bar, moving_layer);\n\treturn true;\n}\n\nbool handle_ipc_readable(struct swaybar *bar) {\n\tstruct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);\n\tif (!resp) {\n\t\treturn false;\n\t}\n\n\t// The default depth of 32 is too small to represent some nested layouts, but\n\t// we can't pass INT_MAX here because json-c (as of this writing) prefaults\n\t// all the memory for its stack.\n\tjson_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);\n\tif (!tok) {\n\t\tsway_log_errno(SWAY_ERROR, \"failed to create tokener\");\n\t\tfree_ipc_response(resp);\n\t\treturn false;\n\t}\n\n\tjson_object *result = json_tokener_parse_ex(tok, resp->payload, -1);\n\tenum json_tokener_error err = json_tokener_get_error(tok);\n\tjson_tokener_free(tok);\n\n\tif (err != json_tokener_success) {\n\t\tsway_log(SWAY_ERROR, \"failed to parse payload as json: %s\",\n\t\t\t\tjson_tokener_error_desc(err));\n\t\tfree_ipc_response(resp);\n\t\treturn false;\n\t}\n\n\tbool bar_is_dirty = true;\n\tswitch (resp->type) {\n\tcase IPC_EVENT_WORKSPACE:\n\t\tbar_is_dirty = ipc_get_workspaces(bar);\n\t\tbreak;\n\tcase IPC_EVENT_MODE: {\n\t\tjson_object *json_change, *json_pango_markup;\n\t\tif (json_object_object_get_ex(result, \"change\", &json_change)) {\n\t\t\tconst char *change = json_object_get_string(json_change);\n\t\t\tfree(bar->mode);\n\t\t\tbar->mode = strcmp(change, \"default\") != 0 ? strdup(change) : NULL;\n\t\t\tbar->visible_by_mode = bar->mode != NULL;\n\t\t\tdetermine_bar_visibility(bar, false);\n\t\t} else {\n\t\t\tsway_log(SWAY_ERROR, \"failed to parse response\");\n\t\t\tbar_is_dirty = false;\n\t\t\tbreak;\n\t\t}\n\t\tif (json_object_object_get_ex(result,\n\t\t\t\t\t\"pango_markup\", &json_pango_markup)) {\n\t\t\tbar->mode_pango_markup = json_object_get_boolean(json_pango_markup);\n\t\t}\n\t\tbreak;\n\t}\n\tcase IPC_EVENT_BARCONFIG_UPDATE:\n\t\tbar_is_dirty = handle_barconfig_update(bar, resp->payload, result);\n\t\tbreak;\n\tcase IPC_EVENT_BAR_STATE_UPDATE:\n\t\tbar_is_dirty = handle_bar_state_update(bar, result);\n\t\tbreak;\n\tdefault:\n\t\tbar_is_dirty = false;\n\t\tbreak;\n\t}\n\tjson_object_put(result);\n\tfree_ipc_response(resp);\n\treturn bar_is_dirty;\n}\n"
  },
  {
    "path": "swaybar/main.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n#include <getopt.h>\n#include \"swaybar/bar.h\"\n#include \"ipc-client.h\"\n#include \"log.h\"\n\nstatic struct swaybar swaybar;\n\nvoid sig_handler(int signal) {\n\tswaybar.running = false;\n}\n\nint main(int argc, char **argv) {\n\tchar *socket_path = NULL;\n\tbool debug = false;\n\n\tstatic const struct option long_options[] = {\n\t\t{\"help\", no_argument, NULL, 'h'},\n\t\t{\"version\", no_argument, NULL, 'v'},\n\t\t{\"socket\", required_argument, NULL, 's'},\n\t\t{\"bar_id\", required_argument, NULL, 'b'},\n\t\t{\"debug\", no_argument, NULL, 'd'},\n\t\t{0, 0, 0, 0}\n\t};\n\n\tconst char *usage =\n\t\t\"Usage: swaybar [options...]\\n\"\n\t\t\"\\n\"\n\t\t\"  -h, --help             Show help message and quit.\\n\"\n\t\t\"  -v, --version          Show the version number and quit.\\n\"\n\t\t\"  -s, --socket <socket>  Connect to sway via socket.\\n\"\n\t\t\"  -b, --bar_id <id>      Bar ID for which to get the configuration.\\n\"\n\t\t\"  -d, --debug            Enable debugging.\\n\"\n\t\t\"\\n\"\n\t\t\" PLEASE NOTE that swaybar will be automatically started by sway as\\n\"\n\t\t\" soon as there is a 'bar' configuration block in your config file.\\n\"\n\t\t\" You should never need to start it manually.\\n\";\n\n\tint c;\n\twhile (1) {\n\t\tint option_index = 0;\n\t\tc = getopt_long(argc, argv, \"hvs:b:d\", long_options, &option_index);\n\t\tif (c == -1) {\n\t\t\tbreak;\n\t\t}\n\t\tswitch (c) {\n\t\tcase 's': // Socket\n\t\t\tsocket_path = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'b': // Type\n\t\t\tswaybar.id = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tprintf(\"swaybar version \" SWAY_VERSION \"\\n\");\n\t\t\texit(EXIT_SUCCESS);\n\t\t\tbreak;\n\t\tcase 'd': // Debug\n\t\t\tdebug = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfprintf(stderr, \"%s\", usage);\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t}\n\n\tif (debug) {\n\t\tsway_log_init(SWAY_DEBUG, NULL);\n\t} else {\n\t\tsway_log_init(SWAY_INFO, NULL);\n\t}\n\n\tif (!swaybar.id) {\n\t\tsway_log(SWAY_ERROR, \"No bar_id passed. \"\n\t\t\t\t\"Provide --bar_id or let sway start swaybar\");\n\t\treturn 1;\n\t}\n\n\tif (!socket_path) {\n\t\tsocket_path = get_socketpath();\n\t\tif (!socket_path) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to retrieve socket path\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tif (!bar_setup(&swaybar, socket_path)) {\n\t\tfree(socket_path);\n\t\treturn 1;\n\t}\n\n\tfree(socket_path);\n\n\tstruct sigaction sa = { .sa_handler = sig_handler };\n\tsigaction(SIGINT, &sa, NULL);\n\tsigaction(SIGTERM, &sa, NULL);\n\n\tswaybar.running = true;\n\tbar_run(&swaybar);\n\tbar_teardown(&swaybar);\n\treturn 0;\n}\n"
  },
  {
    "path": "swaybar/meson.build",
    "content": "tray_files = have_tray ? [\n\t'tray/host.c',\n\t'tray/icon.c',\n\t'tray/item.c',\n\t'tray/tray.c',\n\t'tray/watcher.c'\n] : []\n\nswaybar_deps = [\n\tcairo,\n\tgdk_pixbuf,\n\tjsonc,\n\tmath,\n\tpango,\n\tpangocairo,\n\trt,\n\twayland_client,\n\twayland_cursor\n]\nif have_tray\n\tswaybar_deps += sdbus\nendif\n\nexecutable(\n\t'swaybar', [\n\t\t'bar.c',\n\t\t'config.c',\n\t\t'i3bar.c',\n\t\t'image.c',\n\t\t'input.c',\n\t\t'ipc.c',\n\t\t'main.c',\n\t\t'render.c',\n\t\t'status_line.c',\n\t\ttray_files,\n\t\twl_protos_src,\n\t],\n\tinclude_directories: [sway_inc],\n\tdependencies: swaybar_deps,\n\tlink_with: [lib_sway_common, lib_sway_client],\n\tinstall: true\n)\n"
  },
  {
    "path": "swaybar/render.c",
    "content": "#include <assert.h>\n#include <linux/input-event-codes.h>\n#include <limits.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include \"cairo_util.h\"\n#include \"pango.h\"\n#include \"pool-buffer.h\"\n#include \"swaybar/bar.h\"\n#include \"swaybar/config.h\"\n#include \"swaybar/i3bar.h\"\n#include \"swaybar/ipc.h\"\n#include \"swaybar/render.h\"\n#include \"swaybar/status_line.h\"\n#if HAVE_TRAY\n#include \"swaybar/tray/tray.h\"\n#endif\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n\nstatic const int WS_HORIZONTAL_PADDING = 5;\nstatic const double WS_VERTICAL_PADDING = 1.5;\nstatic const int BORDER_WIDTH = 1;\n\nstruct render_context {\n\tcairo_t *cairo;\n\tstruct swaybar_output *output;\n\tcairo_font_options_t *textaa_sharp;\n\tcairo_font_options_t *textaa_safe;\n\tuint32_t background_color;\n\tbool has_transparency;\n};\n\nstatic void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) {\n\tuint32_t salpha = fontcolor & 0xFF;\n\tuint32_t balpha = ctx->background_color & 0xFF;\n\n\t// Subpixel antialiasing requires blend be done in cairo, not compositor\n\tcairo_font_options_t *fo = salpha == balpha ?\n\t\t\tctx->textaa_sharp : ctx->textaa_safe;\n\tcairo_set_font_options(ctx->cairo, fo);\n\n\t// Color emojis, being semitransparent bitmaps, are leaky with 'SOURCE'\n\tcairo_operator_t op = salpha == 0xFF ?\n\t\t\tCAIRO_OPERATOR_OVER : CAIRO_OPERATOR_SOURCE;\n\tcairo_set_operator(ctx->cairo, op);\n}\n\nstatic uint32_t render_status_line_error(struct render_context *ctx, double *x) {\n\tstruct swaybar_output *output = ctx->output;\n\tconst char *error = output->bar->status->text;\n\tif (!error) {\n\t\treturn 0;\n\t}\n\n\tuint32_t height = output->height;\n\n\tcairo_t *cairo = ctx->cairo;\n\tcairo_set_source_u32(cairo, 0xFF0000FF);\n\n\tint margin = 3;\n\tdouble ws_vertical_padding = output->bar->config->status_padding;\n\n\tPangoFontDescription *font = output->bar->config->font_description;\n\tint text_width, text_height;\n\tget_text_size(cairo, font, &text_width, &text_height, NULL,\n\t\t\t1, false, \"%s\", error);\n\n\tuint32_t ideal_height = text_height + ws_vertical_padding * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (!output->bar->config->height &&\n\t\t\toutput->height < ideal_surface_height) {\n\t\treturn ideal_surface_height;\n\t}\n\t*x -= text_width + margin;\n\n\tdouble text_y = height / 2.0 - text_height / 2.0;\n\tcairo_move_to(cairo, *x, (int)floor(text_y));\n\tchoose_text_aa_mode(ctx, 0xFF0000FF);\n\trender_text(cairo, font, 1, false, \"%s\", error);\n\t*x -= margin;\n\treturn output->height;\n}\n\nstatic uint32_t render_status_line_text(struct render_context *ctx, double *x) {\n\tstruct swaybar_output *output = ctx->output;\n\tconst char *text = output->bar->status->text;\n\tif (!text) {\n\t\treturn 0;\n\t}\n\n\tcairo_t *cairo = ctx->cairo;\n\tstruct swaybar_config *config = output->bar->config;\n\tuint32_t fontcolor = output->focused ?\n\t\t\tconfig->colors.focused_statusline : config->colors.statusline;\n\tcairo_set_source_u32(cairo, fontcolor);\n\n\tint text_width, text_height;\n\tget_text_size(cairo, config->font_description, &text_width, &text_height, NULL,\n\t\t\t1, config->pango_markup, \"%s\", text);\n\n\tdouble ws_vertical_padding = config->status_padding;\n\tint margin = 3;\n\n\tuint32_t ideal_height = text_height + ws_vertical_padding * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (!output->bar->config->height &&\n\t\t\toutput->height < ideal_surface_height) {\n\t\treturn ideal_surface_height;\n\t}\n\n\t*x -= text_width + margin;\n\tuint32_t height = output->height;\n\tdouble text_y = height / 2.0 - text_height / 2.0;\n\tcairo_move_to(cairo, *x, (int)floor(text_y));\n\tchoose_text_aa_mode(ctx, fontcolor);\n\trender_text(cairo, config->font_description, 1, config->pango_markup, \"%s\", text);\n\t*x -= margin;\n\treturn output->height;\n}\n\nstatic void render_sharp_rectangle(cairo_t *cairo, uint32_t color,\n\t\tdouble x, double y, double width, double height) {\n\tcairo_save(cairo);\n\tcairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);\n\tcairo_set_source_u32(cairo, color);\n\tcairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);\n\tcairo_rectangle(cairo, x, y, width, height);\n\tcairo_fill(cairo);\n\tcairo_restore(cairo);\n}\n\nstatic void render_sharp_line(cairo_t *cairo, uint32_t color,\n\t\tdouble x, double y, double width, double height) {\n\tif (width > 1 && height > 1) {\n\t\trender_sharp_rectangle(cairo, color, x, y, width, height);\n\t} else {\n\t\tcairo_save(cairo);\n\t\tcairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);\n\t\tcairo_set_source_u32(cairo, color);\n\t\tcairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);\n\t\tif (width == 1) {\n\t\t\tx += 0.5;\n\t\t\theight += y;\n\t\t\twidth = x;\n\t\t}\n\t\tif (height == 1) {\n\t\t\ty += 0.5;\n\t\t\twidth += x;\n\t\t\theight = y;\n\t\t}\n\t\tcairo_move_to(cairo, x, y);\n\t\tcairo_set_line_width(cairo, 1.0);\n\t\tcairo_line_to(cairo, width, height);\n\t\tcairo_stroke(cairo);\n\t\tcairo_restore(cairo);\n\t}\n}\n\nstatic enum hotspot_event_handling block_hotspot_callback(\n\t\tstruct swaybar_output *output, struct swaybar_hotspot *hotspot,\n\t\tdouble x, double y, uint32_t button, bool released, void *data) {\n\tstruct i3bar_block *block = data;\n\tstruct status_line *status = output->bar->status;\n\treturn i3bar_block_send_click(status, block, x, y,\n\t\t\tx - (double)hotspot->x,\n\t\t\ty - (double)hotspot->y,\n\t\t\t(double)hotspot->width,\n\t\t\t(double)hotspot->height,\n\t\t\toutput->scale, button, released);\n}\n\nstatic void i3bar_block_unref_callback(void *data) {\n\ti3bar_block_unref(data);\n}\n\nstatic uint32_t render_status_block(struct render_context *ctx,\n\t\tstruct i3bar_block *block, double *x, bool edge, bool use_short_text) {\n\tif (!block->full_text || !*block->full_text) {\n\t\treturn 0;\n\t}\n\n\tchar* text = block->full_text;\n\tif (use_short_text && block->short_text && *block->short_text) {\n\t\ttext = block->short_text;\n\t}\n\n\tcairo_t *cairo = ctx->cairo;\n\tstruct swaybar_output *output = ctx->output;\n\tstruct swaybar_config *config = output->bar->config;\n\tint text_width, text_height;\n\tget_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,\n\t\t\tblock->markup, \"%s\", text);\n\n\tint margin = 3;\n\tdouble ws_vertical_padding = config->status_padding;\n\n\tint width = text_width;\n\tif (block->min_width_str) {\n\t\tint w;\n\t\tget_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup,\n\t\t\t\t\"%s\", block->min_width_str);\n\t\tblock->min_width = w;\n\t}\n\tif (width < block->min_width) {\n\t\twidth = block->min_width;\n\t}\n\n\tdouble block_width = width;\n\tuint32_t ideal_height = text_height + ws_vertical_padding * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (!output->bar->config->height &&\n\t\t\toutput->height < ideal_surface_height) {\n\t\treturn ideal_surface_height;\n\t}\n\n\t*x -= width;\n\tif ((block->border_set || block->urgent) && block->border_left > 0) {\n\t\t*x -= (block->border_left + margin);\n\t\tblock_width += block->border_left + margin;\n\t}\n\tif ((block->border_set || block->urgent) && block->border_right > 0) {\n\t\t*x -= (block->border_right + margin);\n\t\tblock_width += block->border_right + margin;\n\t}\n\n\tint sep_width, sep_height;\n\tint sep_block_width = block->separator_block_width;\n\tif (!edge) {\n\t\tif (config->sep_symbol) {\n\t\t\tget_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,\n\t\t\t\t\t1, false, \"%s\", config->sep_symbol);\n\t\t\tuint32_t _ideal_height = sep_height + ws_vertical_padding * 2;\n\t\t\tuint32_t _ideal_surface_height = _ideal_height;\n\t\t\tif (!output->bar->config->height &&\n\t\t\t\t\toutput->height < _ideal_surface_height) {\n\t\t\t\treturn _ideal_surface_height;\n\t\t\t}\n\t\t\tif (block->separator && sep_width > sep_block_width) {\n\t\t\t\tsep_block_width = sep_width + margin * 2;\n\t\t\t}\n\t\t}\n\t\t*x -= sep_block_width;\n\t} else if (config->status_edge_padding) {\n\t\t*x -= config->status_edge_padding;\n\t}\n\n\tuint32_t height = output->height;\n\tif (output->bar->status->click_events) {\n\t\tstruct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));\n\t\thotspot->x = *x;\n\t\thotspot->y = 0;\n\t\thotspot->width = width;\n\t\thotspot->height = height;\n\t\thotspot->callback = block_hotspot_callback;\n\t\thotspot->destroy = i3bar_block_unref_callback;\n\t\thotspot->data = block;\n\t\tblock->ref_count++;\n\t\twl_list_insert(&output->hotspots, &hotspot->link);\n\t}\n\n\tdouble x_pos = *x;\n\tdouble y_pos = ws_vertical_padding;\n\tdouble render_height = height - ws_vertical_padding * 2;\n\n\tuint32_t bg_color = block->urgent\n\t\t? config->colors.urgent_workspace.background : block->background;\n\tctx->has_transparency |= (bg_color & 0xFF) != 0xFF;\n\tif (bg_color) {\n\t\trender_sharp_rectangle(cairo, bg_color, x_pos, y_pos,\n\t\t\t\tblock_width, render_height);\n\t\tctx->background_color = bg_color;\n\t}\n\n\tuint32_t border_color = block->urgent\n\t\t? config->colors.urgent_workspace.border : block->border;\n\tif (block->border_set || block->urgent) {\n\t\tif (block->border_top > 0) {\n\t\t\trender_sharp_line(cairo, border_color, x_pos, y_pos,\n\t\t\t\t\tblock_width, block->border_top);\n\t\t}\n\t\tif (block->border_bottom > 0) {\n\t\t\trender_sharp_line(cairo, border_color, x_pos,\n\t\t\t\t\ty_pos + render_height - block->border_bottom,\n\t\t\t\t\tblock_width, block->border_bottom);\n\t\t}\n\t\tif (block->border_left > 0) {\n\t\t\trender_sharp_line(cairo, border_color, x_pos, y_pos,\n\t\t\t\t\tblock->border_left, render_height);\n\t\t}\n\t\tx_pos += block->border_left + margin;\n\t}\n\n\tdouble offset = 0;\n\tif (has_prefix(block->align, \"left\")) {\n\t\toffset = x_pos;\n\t} else if (has_prefix(block->align, \"right\")) {\n\t\toffset = x_pos + width - text_width;\n\t} else if (has_prefix(block->align, \"center\")) {\n\t\toffset = x_pos + (width - text_width) / 2;\n\t}\n\tdouble text_y = height / 2.0 - text_height / 2.0;\n\tcairo_move_to(cairo, offset, (int)floor(text_y));\n\tuint32_t color = output->focused ?\n\t\tconfig->colors.focused_statusline : config->colors.statusline;\n\tcolor = block->color_set ? block->color : color;\n\tcolor = block->urgent ? config->colors.urgent_workspace.text : color;\n\tcairo_set_source_u32(cairo, color);\n\tchoose_text_aa_mode(ctx, color);\n\trender_text(cairo, config->font_description, 1, block->markup, \"%s\", text);\n\tx_pos += width;\n\n\tif (block->border_set || block->urgent) {\n\t\tx_pos += margin;\n\t\tif (block->border_right > 0) {\n\t\t\trender_sharp_line(cairo, border_color, x_pos, y_pos,\n\t\t\t\t\tblock->border_right, render_height);\n\t\t}\n\t\tx_pos += block->border_right;\n\t}\n\n\tif (!edge && block->separator) {\n\t\tif (output->focused) {\n\t\t\tcolor = config->colors.focused_separator;\n\t\t} else {\n\t\t\tcolor = config->colors.separator;\n\t\t}\n\t\tcairo_set_source_u32(cairo, color);\n\t\tif (config->sep_symbol) {\n\t\t\toffset = x_pos + (sep_block_width - sep_width) / 2;\n\t\t\tdouble sep_y = height / 2.0 - sep_height / 2.0;\n\t\t\tcairo_move_to(cairo, offset, (int)floor(sep_y));\n\t\t\tchoose_text_aa_mode(ctx, color);\n\t\t\trender_text(cairo, config->font_description, 1, false,\n\t\t\t\t\t\"%s\", config->sep_symbol);\n\t\t} else {\n\t\t\tcairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);\n\t\t\tcairo_set_line_width(cairo, 1);\n\t\t\tcairo_move_to(cairo, x_pos + sep_block_width / 2, margin);\n\t\t\tcairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);\n\t\t\tcairo_stroke(cairo);\n\t\t}\n\t}\n\treturn output->height;\n}\n\nstatic void predict_status_block_pos(cairo_t *cairo,\n\t\tstruct swaybar_output *output, struct i3bar_block *block, double *x,\n\t\tbool edge) {\n\tif (!block->full_text || !*block->full_text) {\n\t\treturn;\n\t}\n\n\tstruct swaybar_config *config = output->bar->config;\n\n\tint text_width, text_height;\n\tget_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,\n\t\t\tblock->markup, \"%s\", block->full_text);\n\n\tint margin = 3;\n\tdouble ws_vertical_padding = config->status_padding;\n\n\tint width = text_width;\n\n\tif (block->min_width_str) {\n\t\tint w;\n\t\tget_text_size(cairo, config->font_description, &w, NULL, NULL,\n\t\t\t\t1, block->markup, \"%s\", block->min_width_str);\n\t\tblock->min_width = w;\n\t}\n\tif (width < block->min_width) {\n\t\twidth = block->min_width;\n\t}\n\n\tuint32_t ideal_height = text_height + ws_vertical_padding * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (!output->bar->config->height &&\n\t\t\toutput->height < ideal_surface_height) {\n\t\treturn;\n\t}\n\n\t*x -= width;\n\tif ((block->border_set || block->urgent) && block->border_left > 0) {\n\t\t*x -= (block->border_left + margin);\n\t}\n\tif ((block->border_set || block->urgent) && block->border_right > 0) {\n\t\t*x -= (block->border_right + margin);\n\t}\n\n\tint sep_width, sep_height;\n\tint sep_block_width = block->separator_block_width;\n\tif (!edge) {\n\t\tif (config->sep_symbol) {\n\t\t\tget_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,\n\t\t\t\t\t1, false, \"%s\", config->sep_symbol);\n\t\t\tuint32_t _ideal_height = sep_height + ws_vertical_padding * 2;\n\t\t\tuint32_t _ideal_surface_height = _ideal_height;\n\t\t\tif (!output->bar->config->height &&\n\t\t\t\t\toutput->height < _ideal_surface_height) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (sep_width > sep_block_width) {\n\t\t\t\tsep_block_width = sep_width + margin * 2;\n\t\t\t}\n\t\t}\n\t\t*x -= sep_block_width;\n\t} else if (config->status_edge_padding) {\n\t\t*x -= config->status_edge_padding;\n\t}\n}\n\nstatic double predict_status_line_pos(cairo_t *cairo,\n\t\tstruct swaybar_output *output, double x) {\n\tbool edge = x == output->width;\n\tstruct i3bar_block *block;\n\twl_list_for_each(block, &output->bar->status->blocks, link) {\n\t\tpredict_status_block_pos(cairo, output, block, &x, edge);\n\t\tedge = false;\n\t}\n\treturn x;\n}\n\nstatic uint32_t predict_workspace_button_length(cairo_t *cairo,\n\t\tstruct swaybar_output *output,\n\t\tstruct swaybar_workspace *ws) {\n\tstruct swaybar_config *config = output->bar->config;\n\n\tint text_width, text_height;\n\tget_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,\n\t\t\tconfig->pango_markup, \"%s\", ws->label);\n\n\tint ws_vertical_padding = WS_VERTICAL_PADDING;\n\tint ws_horizontal_padding = WS_HORIZONTAL_PADDING;\n\tint border_width = BORDER_WIDTH;\n\n\tuint32_t ideal_height = ws_vertical_padding * 2 + text_height\n\t\t+ border_width * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (!output->bar->config->height &&\n\t\t\toutput->height < ideal_surface_height) {\n\t\treturn 0;\n\t}\n\n\tuint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;\n\tif (width < config->workspace_min_width) {\n\t\twidth = config->workspace_min_width;\n\t}\n\treturn width;\n}\n\nstatic uint32_t predict_workspace_buttons_length(cairo_t *cairo,\n\t\tstruct swaybar_output *output) {\n\tuint32_t width = 0;\n\tif (output->bar->config->workspace_buttons) {\n\t\tstruct swaybar_workspace *ws;\n\t\twl_list_for_each(ws, &output->workspaces, link) {\n\t\t\twidth += predict_workspace_button_length(cairo, output, ws);\n\t\t}\n\t}\n\treturn width;\n}\n\nstatic uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,\n\t\tstruct swaybar_output *output) {\n\tconst char *mode = output->bar->mode;\n\tif (!mode) {\n\t\treturn 0;\n\t}\n\n\tstruct swaybar_config *config = output->bar->config;\n\n\tif (!config->binding_mode_indicator) {\n\t\treturn 0;\n\t}\n\n\tint text_width, text_height;\n\tget_text_size(cairo, config->font_description, &text_width, &text_height, NULL,\n\t\t\t1, output->bar->mode_pango_markup,\n\t\t\t\"%s\", mode);\n\n\tint ws_vertical_padding = WS_VERTICAL_PADDING;\n\tint ws_horizontal_padding = WS_HORIZONTAL_PADDING;\n\tint border_width = BORDER_WIDTH;\n\n\tuint32_t ideal_height = text_height + ws_vertical_padding * 2\n\t\t+ border_width * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (!output->bar->config->height &&\n\t\t\toutput->height < ideal_surface_height) {\n\t\treturn 0;\n\t}\n\tuint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;\n\tif (width < config->workspace_min_width) {\n\t\twidth = config->workspace_min_width;\n\t}\n\treturn width;\n}\n\nstatic uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {\n\tstruct swaybar_output *output = ctx->output;\n\tuint32_t max_height = 0;\n\tbool edge = *x == output->width;\n\tstruct i3bar_block *block;\n\tbool use_short_text = false;\n\n\tcairo_t *cairo = ctx->cairo;\n\tdouble reserved_width =\n\t\t\tpredict_workspace_buttons_length(cairo, output) +\n\t\t\tpredict_binding_mode_indicator_length(cairo, output) +\n\t\t\t3; // require a bit of space for margin\n\n\tdouble predicted_full_pos =\n\t\t\tpredict_status_line_pos(cairo, output, *x);\n\n\tif (predicted_full_pos < reserved_width) {\n\t\tuse_short_text = true;\n\t}\n\n\twl_list_for_each(block, &output->bar->status->blocks, link) {\n\t\tuint32_t h = render_status_block(ctx, block, x, edge,\n\t\t\t\t\tuse_short_text);\n\t\tmax_height = h > max_height ? h : max_height;\n\t\tedge = false;\n\t}\n\treturn max_height;\n}\n\nstatic uint32_t render_status_line(struct render_context *ctx, double *x) {\n\tstruct status_line *status = ctx->output->bar->status;\n\tswitch (status->protocol) {\n\tcase PROTOCOL_ERROR:\n\t\treturn render_status_line_error(ctx, x);\n\tcase PROTOCOL_TEXT:\n\t\treturn render_status_line_text(ctx, x);\n\tcase PROTOCOL_I3BAR:\n\t\treturn render_status_line_i3bar(ctx, x);\n\tcase PROTOCOL_UNDEF:\n\t\treturn 0;\n\t}\n\treturn 0;\n}\n\nstatic struct box_size render_box(struct render_context *ctx, double x,\n\t\tstruct box_colors colors, const char *label, bool pango_markup) {\n\tstruct swaybar_output *output = ctx->output;\n\tstruct swaybar_config *config = output->bar->config;\n\tcairo_t *cairo = ctx->cairo;\n\n\tint text_width, text_height;\n\tget_text_size(cairo, config->font_description, &text_width, &text_height, NULL,\n\t\t\t1, pango_markup, \"%s\", label);\n\n\tuint32_t width = text_width + WS_HORIZONTAL_PADDING * 2 + BORDER_WIDTH * 2;\n\tif (width < config->workspace_min_width) {\n\t\twidth = config->workspace_min_width;\n\t}\n\n\tuint32_t ideal_height = text_height + WS_VERTICAL_PADDING * 2\n\t\t+ BORDER_WIDTH * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (!output->bar->config->height &&\n\t\t\toutput->height < ideal_surface_height) {\n\t\treturn (struct box_size) {\n\t\t\t.width = width,\n\t\t\t.height = ideal_surface_height,\n\t\t};\n\t}\n\n\tuint32_t height = output->height;\n\tcairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);\n\tcairo_set_source_u32(cairo, colors.background);\n\tctx->background_color = colors.background;\n\tctx->has_transparency |= (colors.background & 0xFF) != 0xFF;\n\tcairo_rectangle(cairo, x, 0, width, height);\n\tcairo_fill(cairo);\n\n\tcairo_set_source_u32(cairo, colors.border);\n\tcairo_rectangle(cairo, x, 0, width, BORDER_WIDTH);\n\tcairo_fill(cairo);\n\tcairo_rectangle(cairo, x, 0, BORDER_WIDTH, height);\n\tcairo_fill(cairo);\n\tcairo_rectangle(cairo, x + width - BORDER_WIDTH, 0, BORDER_WIDTH, height);\n\tcairo_fill(cairo);\n\tcairo_rectangle(cairo, x, height - BORDER_WIDTH, width, BORDER_WIDTH);\n\tcairo_fill(cairo);\n\n\tdouble text_y = height / 2.0 - text_height / 2.0;\n\tcairo_set_source_u32(cairo, colors.text);\n\tcairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));\n\tchoose_text_aa_mode(ctx, colors.text);\n\trender_text(cairo, config->font_description, 1, pango_markup,\n\t\t\t\"%s\", label);\n\n\treturn (struct box_size) {\n\t\t.width = width,\n\t\t.height = output->height,\n\t};\n}\n\nstatic uint32_t render_binding_mode_indicator(struct render_context *ctx,\n\t\tdouble x) {\n\tstruct swaybar_output *output = ctx->output;\n\tconst char *mode = output->bar->mode;\n\tif (!mode) {\n\t\treturn 0;\n\t}\n\n\tstruct box_size size = render_box(ctx, x, output->bar->config->colors.binding_mode,\n\t\t\tmode, output->bar->mode_pango_markup);\n\treturn size.height;\n}\n\nstatic enum hotspot_event_handling workspace_hotspot_callback(\n\t\tstruct swaybar_output *output, struct swaybar_hotspot *hotspot,\n\t\tdouble x, double y, uint32_t button, bool released, void *data) {\n\tif (button != BTN_LEFT) {\n\t\treturn HOTSPOT_PROCESS;\n\t}\n\tif (released) {\n\t\t// Since we handle the pressed event, also handle the released event\n\t\t// to block it from falling through to a binding in the bar\n\t\treturn HOTSPOT_IGNORE;\n\t}\n\tipc_send_workspace_command(output->bar, (const char *)data);\n\treturn HOTSPOT_IGNORE;\n}\n\nstatic uint32_t render_workspace_button(struct render_context *ctx,\n\t\tstruct swaybar_workspace *ws, double *x) {\n\tstruct swaybar_output *output = ctx->output;\n\tstruct swaybar_config *config = output->bar->config;\n\n\tstruct box_colors box_colors;\n\tif (ws->urgent) {\n\t\tbox_colors = config->colors.urgent_workspace;\n\t} else if (ws->focused) {\n\t\tbox_colors = config->colors.focused_workspace;\n\t} else if (ws->visible) {\n\t\tbox_colors = config->colors.active_workspace;\n\t} else {\n\t\tbox_colors = config->colors.inactive_workspace;\n\t}\n\n\tstruct box_size size = render_box(ctx, *x, box_colors,\n\t\t\tws->label, config->pango_markup);\n\n\tstruct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));\n\thotspot->x = *x;\n\thotspot->y = 0;\n\thotspot->width = size.width;\n\thotspot->height = size.height;\n\thotspot->callback = workspace_hotspot_callback;\n\thotspot->destroy = free;\n\thotspot->data = strdup(ws->name);\n\twl_list_insert(&output->hotspots, &hotspot->link);\n\n\t*x += size.width;\n\treturn size.height;\n}\n\nstatic uint32_t render_to_cairo(struct render_context *ctx) {\n\tcairo_t *cairo = ctx->cairo;\n\tstruct swaybar_output *output = ctx->output;\n\tstruct swaybar *bar = output->bar;\n\tstruct swaybar_config *config = bar->config;\n\n\tint th;\n\tget_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, \"\");\n\tuint32_t max_height = (th + WS_VERTICAL_PADDING * 4);\n\t/*\n\t * Each render_* function takes the actual height of the bar, and returns\n\t * the ideal height. If the actual height is too short, the render function\n\t * can do whatever it wants - the buffer won't be committed. If the actual\n\t * height is too tall, the render function should adapt its drawing to\n\t * utilize the available space.\n\t */\n\tdouble x = output->width;\n#if HAVE_TRAY\n\tif (bar->tray) {\n\t\tuint32_t h = render_tray(cairo, output, &x);\n\t\tmax_height = h > max_height ? h : max_height;\n\t}\n#endif\n\tif (bar->status) {\n\t\tuint32_t h = render_status_line(ctx, &x);\n\t\tmax_height = h > max_height ? h : max_height;\n\t}\n\tx = 0;\n\tif (config->workspace_buttons) {\n\t\tstruct swaybar_workspace *ws;\n\t\twl_list_for_each(ws, &output->workspaces, link) {\n\t\t\tuint32_t h = render_workspace_button(ctx, ws, &x);\n\t\t\tmax_height = h > max_height ? h : max_height;\n\t\t}\n\t}\n\tif (config->binding_mode_indicator) {\n\t\tuint32_t h = render_binding_mode_indicator(ctx, x);\n\t\tmax_height = h > max_height ? h : max_height;\n\t}\n\n\treturn max_height > output->height ? max_height : output->height;\n}\n\nstatic void output_frame_handle_done(void *data, struct wl_callback *callback,\n\t\tuint32_t time) {\n\twl_callback_destroy(callback);\n\tstruct swaybar_output *output = data;\n\toutput->frame_scheduled = false;\n\tif (output->dirty) {\n\t\trender_frame(output);\n\t\toutput->dirty = false;\n\t}\n}\n\nstatic const struct wl_callback_listener output_frame_listener = {\n\t.done = output_frame_handle_done\n};\n\nvoid render_frame(struct swaybar_output *output) {\n\tassert(output->surface != NULL);\n\tif (!output->layer_surface) {\n\t\treturn;\n\t}\n\n\tfree_hotspots(&output->hotspots);\n\n\tuint32_t background_color;\n\tif (output->focused) {\n\t\tbackground_color = output->bar->config->colors.focused_background;\n\t} else {\n\t\tbackground_color = output->bar->config->colors.background;\n\t}\n\n\tstruct render_context ctx = {\n\t\t.output = output,\n\t\t// initial background color used for deciding the best way to antialias text\n\t\t.background_color = background_color,\n\t\t.has_transparency = (background_color & 0xFF) != 0xFF,\n\t};\n\n\tcairo_surface_t *recorder = cairo_recording_surface_create(\n\t\t\tCAIRO_CONTENT_COLOR_ALPHA, NULL);\n\tcairo_t *cairo = cairo_create(recorder);\n\tcairo_scale(cairo, output->scale, output->scale);\n\tcairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);\n\tctx.cairo = cairo;\n\n\tcairo_font_options_t *fo = cairo_font_options_create();\n\tcairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);\n\tctx.textaa_safe = fo;\n\tif (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {\n\t\tctx.textaa_sharp = ctx.textaa_safe;\n\t} else {\n\t\tfo = cairo_font_options_create();\n\t\tcairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);\n\t\tcairo_font_options_set_subpixel_order(fo,\n\t\t\tto_cairo_subpixel_order(output->subpixel));\n\t\tctx.textaa_sharp = fo;\n\t}\n\n\n\tcairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);\n\tcairo_set_source_u32(cairo, background_color);\n\tcairo_paint(cairo);\n\n\tuint32_t height = render_to_cairo(&ctx);\n\tint config_height = output->bar->config->height;\n\tif (config_height > 0) {\n\t\theight = config_height;\n\t}\n\tif (height != output->height || output->width == 0) {\n\t\t// Reconfigure surface\n\t\tzwlr_layer_surface_v1_set_size(output->layer_surface, 0, height);\n\t\tzwlr_layer_surface_v1_set_margin(output->layer_surface,\n\t\t\t\toutput->bar->config->gaps.top,\n\t\t\t\toutput->bar->config->gaps.right,\n\t\t\t\toutput->bar->config->gaps.bottom,\n\t\t\t\toutput->bar->config->gaps.left);\n\t\tif (strcmp(output->bar->config->mode, \"dock\") == 0) {\n\t\t\tzwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height);\n\t\t}\n\t\t// TODO: this could infinite loop if the compositor assigns us a\n\t\t// different height than what we asked for\n\t\twl_surface_commit(output->surface);\n\t} else if (height > 0) {\n\t\t// Replay recording into shm and send it off\n\t\toutput->current_buffer = get_next_buffer(output->bar->shm,\n\t\t\t\toutput->buffers,\n\t\t\t\toutput->width * output->scale,\n\t\t\t\toutput->height * output->scale);\n\t\tif (!output->current_buffer) {\n\t\t\tgoto cleanup;\n\t\t}\n\t\tcairo_t *shm = output->current_buffer->cairo;\n\n\t\tcairo_save(shm);\n\t\tcairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);\n\t\tcairo_paint(shm);\n\t\tcairo_restore(shm);\n\n\t\tcairo_set_source_surface(shm, recorder, 0.0, 0.0);\n\t\tcairo_paint(shm);\n\n\t\twl_surface_set_buffer_scale(output->surface, output->scale);\n\t\twl_surface_attach(output->surface,\n\t\t\t\toutput->current_buffer->buffer, 0, 0);\n\t\twl_surface_damage(output->surface, 0, 0,\n\t\t\t\toutput->width, output->height);\n\n\t\tif (!ctx.has_transparency) {\n\t\t\tstruct wl_region *region =\n\t\t\t\twl_compositor_create_region(output->bar->compositor);\n\t\t\twl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);\n\t\t\twl_surface_set_opaque_region(output->surface, region);\n\t\t\twl_region_destroy(region);\n\t\t} else {\n\t\t\twl_surface_set_opaque_region(output->surface, NULL);\n\t\t}\n\n\t\tstruct wl_callback *frame_callback = wl_surface_frame(output->surface);\n\t\twl_callback_add_listener(frame_callback, &output_frame_listener, output);\n\t\toutput->frame_scheduled = true;\n\n\t\twl_surface_commit(output->surface);\n\t}\n\ncleanup:\n\tif (ctx.textaa_sharp != ctx.textaa_safe) {\n\t\tcairo_font_options_destroy(ctx.textaa_sharp);\n\t}\n\tcairo_font_options_destroy(ctx.textaa_safe);\n\tcairo_surface_destroy(recorder);\n\tcairo_destroy(cairo);\n}\n"
  },
  {
    "path": "swaybar/status_line.c",
    "content": "#include <assert.h>\n#include <fcntl.h>\n#include <sys/ioctl.h>\n#include <json.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include \"log.h\"\n#include \"loop.h\"\n#include \"swaybar/bar.h\"\n#include \"swaybar/config.h\"\n#include \"swaybar/i3bar.h\"\n#include \"swaybar/status_line.h\"\n\nstatic void status_line_close_fds(struct status_line *status) {\n\tif (status->read_fd != -1) {\n\t\tloop_remove_fd(status->bar->eventloop, status->read_fd);\n\t\tclose(status->read_fd);\n\t\tstatus->read_fd = -1;\n\t}\n\tif (status->write_fd != -1) {\n\t\tclose(status->write_fd);\n\t\tstatus->write_fd = -1;\n\t}\n}\n\nvoid status_error(struct status_line *status, const char *text) {\n\tstatus_line_close_fds(status);\n\tstatus->protocol = PROTOCOL_ERROR;\n\tstatus->text = text;\n}\n\nbool status_handle_readable(struct status_line *status) {\n\tssize_t read_bytes = 1;\n\tswitch (status->protocol) {\n\tcase PROTOCOL_UNDEF:\n\t\terrno = 0;\n\t\tint available_bytes;\n\t\tif (ioctl(status->read_fd, FIONREAD, &available_bytes) == -1) {\n\t\t\tsway_log(SWAY_ERROR, \"Unable to read status command output size\");\n\t\t\tstatus_error(status, \"[error reading from status command]\");\n\t\t\treturn true;\n\t\t}\n\n\t\tif ((size_t)available_bytes + 1 > status->buffer_size) {\n\t\t\t// need room for leading '\\0' too\n\t\t\tstatus->buffer_size = available_bytes + 1;\n\t\t\tstatus->buffer = realloc(status->buffer, status->buffer_size);\n\t\t}\n\t\tif (status->buffer == NULL) {\n\t\t\tsway_log_errno(SWAY_ERROR, \"Unable to read status line\");\n\t\t\tstatus_error(status, \"[error reading from status command]\");\n\t\t\treturn true;\n\t\t}\n\n\t\tread_bytes = read(status->read_fd, status->buffer, available_bytes);\n\t\tif (read_bytes != available_bytes) {\n\t\t\tstatus_error(status, \"[error reading from status command]\");\n\t\t\treturn true;\n\t\t}\n\t\tstatus->buffer[available_bytes] = 0;\n\n\t\t// the header must be sent completely the first time round\n\t\tchar *newline = strchr(status->buffer, '\\n');\n\t\tjson_object *header, *version;\n\t\tif (newline != NULL\n\t\t\t\t&& (header = json_tokener_parse(status->buffer))\n\t\t\t\t&& json_object_object_get_ex(header, \"version\", &version)\n\t\t\t\t&& json_object_get_int(version) == 1) {\n\t\t\tsway_log(SWAY_DEBUG, \"Using i3bar protocol.\");\n\t\t\tstatus->protocol = PROTOCOL_I3BAR;\n\n\t\t\tjson_object *click_events;\n\t\t\tif (json_object_object_get_ex(header, \"click_events\", &click_events)\n\t\t\t\t\t&& json_object_get_boolean(click_events)) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Enabling click events.\");\n\t\t\t\tstatus->click_events = true;\n\t\t\t\tif (write(status->write_fd, \"[\\n\", 2) != 2) {\n\t\t\t\t\tstatus_error(status, \"[failed to write to status command]\");\n\t\t\t\t\tjson_object_put(header);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tjson_object *float_event_coords;\n\t\t\tif (json_object_object_get_ex(header, \"float_event_coords\", &float_event_coords)\n\t\t\t\t\t&& json_object_get_boolean(float_event_coords)) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Enabling floating-point coordinates.\");\n\t\t\t\tstatus->float_event_coords = true;\n\t\t\t}\n\n\t\t\tjson_object *signal;\n\t\t\tif (json_object_object_get_ex(header, \"stop_signal\", &signal)) {\n\t\t\t\tstatus->stop_signal = json_object_get_int(signal);\n\t\t\t\tsway_log(SWAY_DEBUG, \"Setting stop signal to %d\", status->stop_signal);\n\t\t\t}\n\t\t\tif (json_object_object_get_ex(header, \"cont_signal\", &signal)) {\n\t\t\t\tstatus->cont_signal = json_object_get_int(signal);\n\t\t\t\tsway_log(SWAY_DEBUG, \"Setting cont signal to %d\", status->cont_signal);\n\t\t\t}\n\n\t\t\tjson_object_put(header);\n\n\t\t\twl_list_init(&status->blocks);\n\t\t\tstatus->tokener = json_tokener_new();\n\t\t\tstatus->buffer_index = strlen(newline + 1);\n\t\t\tmemmove(status->buffer, newline + 1, status->buffer_index + 1);\n\t\t\treturn i3bar_handle_readable(status);\n\t\t}\n\n\t\tsway_log(SWAY_DEBUG, \"Using text protocol.\");\n\t\tstatus->protocol = PROTOCOL_TEXT;\n\t\tstatus->text = status->buffer;\n\t\t// intentional fall-through\n\tcase PROTOCOL_TEXT:\n\t\twhile (true) {\n\t\t\tif (status->buffer[read_bytes - 1] == '\\n') {\n\t\t\t\tstatus->buffer[read_bytes - 1] = '\\0';\n\t\t\t}\n\t\t\terrno = 0;\n\t\t\tread_bytes = getline(&status->buffer,\n\t\t\t\t\t&status->buffer_size, status->read);\n\t\t\tif (errno == EAGAIN) {\n\t\t\t\tclearerr(status->read);\n\t\t\t\treturn true;\n\t\t\t} else if (errno) {\n\t\t\t\tstatus_error(status, \"[error reading from status command]\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\tcase PROTOCOL_I3BAR:\n\t\treturn i3bar_handle_readable(status);\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nstruct status_line *status_line_init(char *cmd) {\n\tstruct status_line *status = calloc(1, sizeof(struct status_line));\n\tstatus->stop_signal = SIGSTOP;\n\tstatus->cont_signal = SIGCONT;\n\n\tstatus->buffer_size = 8192;\n\tstatus->buffer = malloc(status->buffer_size);\n\n\tint pipe_read_fd[2];\n\tint pipe_write_fd[2];\n\tif (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) {\n\t\tsway_log(SWAY_ERROR, \"Unable to create pipes for status_command fork\");\n\t\texit(1);\n\t}\n\n\tassert(!getenv(\"WAYLAND_SOCKET\") && \"display must be initialized before \"\n\t\t\" starting `status-command`; WAYLAND_SOCKET should not be set\");\n\tstatus->pid = fork();\n\tif (status->pid < 0) {\n\t\tsway_log_errno(SWAY_ERROR, \"fork failed\");\n\t\texit(1);\n\t} else if (status->pid == 0) {\n\t\tsetpgid(0, 0);\n\n\t\tdup2(pipe_read_fd[1], STDOUT_FILENO);\n\t\tclose(pipe_read_fd[0]);\n\t\tclose(pipe_read_fd[1]);\n\n\t\tdup2(pipe_write_fd[0], STDIN_FILENO);\n\t\tclose(pipe_write_fd[0]);\n\t\tclose(pipe_write_fd[1]);\n\n\t\tchar *const _cmd[] = { \"sh\", \"-c\", cmd, NULL, };\n\t\texecvp(_cmd[0], _cmd);\n\t\texit(1);\n\t}\n\n\tclose(pipe_read_fd[1]);\n\tstatus->read_fd = pipe_read_fd[0];\n\tfcntl(status->read_fd, F_SETFL, O_NONBLOCK);\n\tclose(pipe_write_fd[0]);\n\tstatus->write_fd = pipe_write_fd[1];\n\tfcntl(status->write_fd, F_SETFL, O_NONBLOCK);\n\n\tstatus->read = fdopen(status->read_fd, \"r\");\n\tstatus->write = fdopen(status->write_fd, \"w\");\n\treturn status;\n}\n\nvoid status_line_free(struct status_line *status) {\n\tstatus_line_close_fds(status);\n\tkill(-status->pid, status->cont_signal);\n\tkill(-status->pid, SIGTERM);\n\twaitpid(status->pid, NULL, 0);\n\tif (status->protocol == PROTOCOL_I3BAR) {\n\t\tstruct i3bar_block *block, *tmp;\n\t\twl_list_for_each_safe(block, tmp, &status->blocks, link) {\n\t\t\twl_list_remove(&block->link);\n\t\t\ti3bar_block_unref(block);\n\t\t}\n\t\tjson_tokener_free(status->tokener);\n\t}\n\tfree(status->buffer);\n\tfree(status);\n}\n"
  },
  {
    "path": "swaybar/swaybar-protocol.7.scd",
    "content": "swaybar-protocol(7)\n\n# NAME\n\nswaybar-protocol - JSON status line protocol for swaybar\n\n# SUMMARY\n\nswaybar defines the following JSON protocol to specify what information is\ndisplayed in the status line on the right side of swaybar. The protocol\ncomprises a header, that is a JSON object, followed by a newline (*0x0A*),\nfollowed by an infinite JSON array that represents the information to display.\nAll communication is done by writing the status line to standard output and\nreading events from standard input.\n\n# HEADER\n\nThe header is a JSON object with support for the following properties (only\n_version_ is required):\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DEFAULT*\n:- *DESCRIPTION*\n|- version\n:  integer\n:  -\n:[ The protocol version to use. Currently, this must be *1*\n|- click_events\n:  boolean\n:  false\n:  Whether to receive click event information to standard input\n|- cont_signal\n:  integer\n:  SIGCONT\n:  The signal that swaybar should send to continue processing\n|- stop_signal\n:  integer\n:  SIGSTOP\n:  The signal that swaybar should send to stop processing\n\n## MINIMAL HEADER\n```\n{\n\t\"version\": 1\n}\n```\n\n## FULL HEADER\n```\n{\n\t\"version\": 1,\n\t\"click_events\": true,\n\t\"cont_signal\": 18,\n\t\"stop_signal\": 19\n}\n```\n\n# BODY\n\nThe body is an infinite array, where each element of the array is a\nrepresentation of the status line at the time that the element was written.\nEach element of the array is itself an array of JSON objects, where each\nobject represents a block in the status line. Each block can have the following\nproperties (only _full_text_ is required):\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- full_text\n:  string\n:[ The text that will be displayed. *If missing, the block will be skipped.*\n|- short_text\n:  string\n:  If given and the text needs to be shortened due to space, this will be\n   displayed instead of _full_text_\n|- color\n:  string\n:  The text color to use in #RRGGBBAA or #RRGGBB notation\n|- background\n:  string\n:  The background color for the block in #RRGGBBAA or #RRGGBB notation\n|- border\n:  string\n:  The border color for the block in #RRGGBBAA or #RRGGBB notation\n|- border_top\n:  integer\n:  The height in pixels of the top border. The default is 1\n|- border_bottom\n:  integer\n:  The height in pixels of the bottom border. The default is 1\n|- border_left\n:  integer\n:  The width in pixels of the left border. The default is 1\n|- border_right\n:  integer\n:  The width in pixels of the right border. The default is 1\n|- min_width\n:  integer or string\n:  The minimum width to use for the block. This can either be given in pixels\n   or a string can be given to allow for it to be calculated based on the\n   width of the string.\n|- align\n:  string\n:  If the text does not span the full width of the block, this specifies how\n   the text should be aligned inside of the block. This can be _left_\n   (default), _right_, or _center_.\n|- name\n:  string\n:  A name for the block. This is only used to identify the block for click\n   events. If set, each block should have a unique _name_ and _instance_ pair.\n|- instance\n:  string\n:  The instance of the _name_ for the block. This is only used to identify the\n   block for click events. If set, each block should have a unique _name_ and\n   _instance_ pair.\n|- urgent\n:  boolean\n:  Whether the block should be displayed as urgent. Currently swaybar utilizes\n   the colors set in the sway config for urgent workspace buttons. See\n   *sway-bar*(5) for more information on bar color configuration.\n|- separator\n:  boolean\n:  Whether the bar separator should be drawn after the block. See *sway-bar*(5)\n   for more information on how to set the separator text.\n|- separator_block_width\n:  integer\n:  The amount of pixels to leave blank after the block. The separator text will\n   be displayed centered in this gap. The default is 9 pixels.\n|- markup\n:  string\n:  The type of markup to use when parsing the text for the block. This can\n   either be _pango_ or _none_ (default).\n\nOther properties may be given and swaybar will ignore any property that it does\nnot know. It is recommended to prefix any custom properties with an underscore\nto make it easier to distinguish them from protocol properties.\n\n## BODY EXAMPLE\n\n```\n[\n\t[\n\t\t{\n\t\t\t\"full_text\": \"25%\",\n\t\t\t\"min_width\": \"100%\",\n\t\t\t\"urgent\": false\n\t\t},\n\t\t{\n\t\t\t\"full_text\": \"Thu 30 May 2019 02:15:15\"\n\t\t}\n\t],\n\t[\n\t\t{\n\t\t\t\"full_text\": \"20%\",\n\t\t\t\"min_width\": \"100%\",\n\t\t\t\"urgent\": false\n\t\t},\n\t\t{\n\t\t\t\"full_text\": \"Thu 30 May 2019 02:20:52\"\n\t\t}\n\t],\n\t[\n\t\t{\n\t\t\t\"full_text\": \"15%\",\n\t\t\t\"min_width\": \"100%\",\n\t\t\t\"urgent\": true\n\t\t},\n\t\t{\n\t\t\t\"full_text\": \"Thu 30 May 2019 02:25:41\"\n\t\t}\n\t],\n\t...\n```\n\n## FULL BLOCK EXAMPLE\n```\n{\n\t\"full_text\": \"Thu 30 May 2019 02:09:15\",\n\t\"short_text\": \"02:09\",\n\t\"color\": \"#ccccccff\",\n\t\"background\": \"#111111ff\",\n\t\"border\": \"#222222ff\",\n\t\"border_top\": 1,\n\t\"border_bottom\": 1,\n\t\"border_left\": 1,\n\t\"border_right\": 1,\n\t\"min_width\": 100,\n\t\"align\": \"center\",\n\t\"name\": \"clock\",\n\t\"instance\": \"edt\",\n\t\"urgent\": false,\n\t\"separator\": true,\n\t\"separator_block_width\": 5,\n\t\"markup\": \"none\"\n}\n```\n\n# CLICK EVENTS\n\nIf requested in the header, swaybar will write a JSON object, that can be read\nfrom standard in, when the user clicks on a block. The event object will have\nthe following properties:\n\n[- *PROPERTY*\n:- *DATA TYPE*\n:- *DESCRIPTION*\n|- name\n:  string\n:[ The name of the block, if set\n|- instance\n:  string\n:  The instance of the block, if set\n|- x\n:  integer\n:  The x location that the click occurred at\n|- y\n:  integer\n:  The y location that the click occurred at\n|- button\n:  integer\n:  The x11 button number for the click. If the button does not have an x11\n   button mapping, this will be _0_.\n|- event\n:  integer\n:  The event code that corresponds to the button for the click\n|- relative_x\n:  integer\n:  The x location of the click relative to the top-left of the block\n|- relative_y\n:  integer\n:  The y location of the click relative to the top-left of the block\n|- width\n:  integer\n:  The width of the block in pixels\n|- height\n:  integer\n:  The height of the block in pixels\n\n*Differences from i3bar's protocol:*\n- _button_ may be _0_ for buttons that do not have x11 button mappings\n- _event_ has been introduced to allow for non-x11 buttons to be represented\n- The _modifiers_ property is not currently added by swaybar\n\n## EXAMPLE\n\n```\n{\n\t\"name\": \"clock\",\n\t\"instance\": \"edt\",\n\t\"x\": 1900,\n\t\"y\": 10,\n\t\"button\": 1,\n\t\"event\": 274,\n\t\"relative_x\": 100,\n\t\"relative_y\": 8,\n\t\"width\": 120,\n\t\"height\": 18\n}\n```\n\n# SEE ALSO\n\n*sway-bar*(5)\n"
  },
  {
    "path": "swaybar/tray/host.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include \"swaybar/bar.h\"\n#include \"swaybar/tray/host.h\"\n#include \"swaybar/tray/item.h\"\n#include \"swaybar/tray/tray.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic const char *watcher_path = \"/StatusNotifierWatcher\";\n\nstatic int cmp_sni_id(const void *item, const void *cmp_to) {\n\tconst struct swaybar_sni *sni = item;\n\treturn strcmp(sni->watcher_id, cmp_to);\n}\n\nstatic void add_sni(struct swaybar_tray *tray, char *id) {\n\tint idx = list_seq_find(tray->items, cmp_sni_id, id);\n\tif (idx == -1) {\n\t\tsway_log(SWAY_INFO, \"Registering Status Notifier Item '%s'\", id);\n\t\tstruct swaybar_sni *sni = create_sni(id, tray);\n\t\tif (sni) {\n\t\t\tlist_add(tray->items, sni);\n\t\t}\n\t}\n}\n\nstatic int handle_sni_registered(sd_bus_message *msg, void *data,\n\t\tsd_bus_error *error) {\n\tchar *id;\n\tint ret = sd_bus_message_read(msg, \"s\", &id);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to parse register SNI message: %s\", strerror(-ret));\n\t}\n\n\tstruct swaybar_tray *tray = data;\n\tadd_sni(tray, id);\n\n\treturn ret;\n}\n\nstatic int handle_sni_unregistered(sd_bus_message *msg, void *data,\n\t\tsd_bus_error *error) {\n\tchar *id;\n\tint ret = sd_bus_message_read(msg, \"s\", &id);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to parse unregister SNI message: %s\", strerror(-ret));\n\t}\n\n\tstruct swaybar_tray *tray = data;\n\tint idx = list_seq_find(tray->items, cmp_sni_id, id);\n\tif (idx != -1) {\n\t\tsway_log(SWAY_INFO, \"Unregistering Status Notifier Item '%s'\", id);\n\t\tdestroy_sni(tray->items->items[idx]);\n\t\tlist_del(tray->items, idx);\n\t\tset_bar_dirty(tray->bar);\n\t}\n\treturn ret;\n}\n\nstatic int get_registered_snis_callback(sd_bus_message *msg, void *data,\n\t\tsd_bus_error *error) {\n\tif (sd_bus_message_is_method_error(msg, NULL)) {\n\t\tconst sd_bus_error *err = sd_bus_message_get_error(msg);\n\t\tsway_log(SWAY_ERROR, \"Failed to get registered SNIs: %s\", err->message);\n\t\treturn -sd_bus_error_get_errno(err);\n\t}\n\n\tint ret = sd_bus_message_enter_container(msg, 'v', NULL);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to read registered SNIs: %s\", strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tchar **ids;\n\tret = sd_bus_message_read_strv(msg, &ids);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to read registered SNIs: %s\", strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tif (ids) {\n\t\tstruct swaybar_tray *tray = data;\n\t\tfor (char **id = ids; *id; ++id) {\n\t\t\tadd_sni(tray, *id);\n\t\t\tfree(*id);\n\t\t}\n\t}\n\n\tfree(ids);\n\treturn ret;\n}\n\nstatic bool register_to_watcher(struct swaybar_host *host) {\n\t// this is called asynchronously in case the watcher is owned by this process\n\tint ret = sd_bus_call_method_async(host->tray->bus, NULL,\n\t\t\thost->watcher_interface, watcher_path, host->watcher_interface,\n\t\t\t\"RegisterStatusNotifierHost\", NULL, NULL, \"s\", host->service);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to send register call: %s\", strerror(-ret));\n\t\treturn false;\n\t}\n\n\tret = sd_bus_call_method_async(host->tray->bus, NULL,\n\t\t\thost->watcher_interface, watcher_path,\n\t\t\t\"org.freedesktop.DBus.Properties\", \"Get\",\n\t\t\tget_registered_snis_callback, host->tray, \"ss\",\n\t\t\thost->watcher_interface, \"RegisteredStatusNotifierItems\");\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to get registered SNIs: %s\", strerror(-ret));\n\t}\n\n\treturn ret >= 0;\n}\n\nstatic int handle_new_watcher(sd_bus_message *msg,\n\t\tvoid *data, sd_bus_error *error) {\n\tchar *service, *old_owner, *new_owner;\n\tint ret = sd_bus_message_read(msg, \"sss\", &service, &old_owner, &new_owner);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to parse owner change message: %s\", strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tif (!*old_owner) {\n\t\tstruct swaybar_host *host = data;\n\t\tif (strcmp(service, host->watcher_interface) == 0) {\n\t\t\tregister_to_watcher(host);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nbool init_host(struct swaybar_host *host, char *protocol,\n\t\tstruct swaybar_tray *tray) {\n\thost->watcher_interface = format_str(\"org.%s.StatusNotifierWatcher\", protocol);\n\tif (!host->watcher_interface) {\n\t\treturn false;\n\t}\n\n\tsd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL;\n\tint ret = sd_bus_match_signal(tray->bus, &reg_slot, host->watcher_interface,\n\t\t\twatcher_path, host->watcher_interface,\n\t\t\t\"StatusNotifierItemRegistered\", handle_sni_registered, tray);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to subscribe to registering events: %s\",\n\t\t\t\tstrerror(-ret));\n\t\tgoto error;\n\t}\n\tret = sd_bus_match_signal(tray->bus, &unreg_slot, host->watcher_interface,\n\t\t\twatcher_path, host->watcher_interface,\n\t\t\t\"StatusNotifierItemUnregistered\", handle_sni_unregistered, tray);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to subscribe to unregistering events: %s\",\n\t\t\t\tstrerror(-ret));\n\t\tgoto error;\n\t}\n\n\tret = sd_bus_match_signal(tray->bus, &watcher_slot, \"org.freedesktop.DBus\",\n\t\t\t\"/org/freedesktop/DBus\", \"org.freedesktop.DBus\", \"NameOwnerChanged\",\n\t\t\thandle_new_watcher, host);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to subscribe to unregistering events: %s\",\n\t\t\t\tstrerror(-ret));\n\t\tgoto error;\n\t}\n\n\tpid_t pid = getpid();\n\thost->service = format_str(\"org.%s.StatusNotifierHost-%d\", protocol, pid);\n\tif (!host->service) {\n\t\tgoto error;\n\t}\n\tret = sd_bus_request_name(tray->bus, host->service, 0);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_DEBUG, \"Failed to acquire service name: %s\", strerror(-ret));\n\t\tgoto error;\n\t}\n\n\thost->tray = tray;\n\tif (!register_to_watcher(host)) {\n\t\tgoto error;\n\t}\n\n\tsd_bus_slot_set_floating(reg_slot, 0);\n\tsd_bus_slot_set_floating(unreg_slot, 0);\n\tsd_bus_slot_set_floating(watcher_slot, 0);\n\n\tsway_log(SWAY_DEBUG, \"Registered %s\", host->service);\n\treturn true;\nerror:\n\tsd_bus_slot_unref(reg_slot);\n\tsd_bus_slot_unref(unreg_slot);\n\tsd_bus_slot_unref(watcher_slot);\n\tfinish_host(host);\n\treturn false;\n}\n\nvoid finish_host(struct swaybar_host *host) {\n\tsd_bus_release_name(host->tray->bus, host->service);\n\tfree(host->service);\n\tfree(host->watcher_interface);\n}\n"
  },
  {
    "path": "swaybar/tray/icon.c",
    "content": "#include <ctype.h>\n#include <dirent.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <wordexp.h>\n#include \"swaybar/tray/icon.h\"\n#include \"config.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n\nstatic int cmp_id(const void *item, const void *cmp_to) {\n\treturn strcmp(item, cmp_to);\n}\n\nstatic bool dir_exists(char *path) {\n\tstruct stat sb;\n\treturn stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);\n}\n\nstatic list_t *get_basedirs(void) {\n\tlist_t *basedirs = create_list();\n\tlist_add(basedirs, strdup(\"$HOME/.icons\")); // deprecated\n\n\tchar *data_home = getenv(\"XDG_DATA_HOME\");\n\tlist_add(basedirs, strdup(data_home && *data_home ?\n\t\t\t\"$XDG_DATA_HOME/icons\" : \"$HOME/.local/share/icons\"));\n\n\tlist_add(basedirs, strdup(\"/usr/share/pixmaps\"));\n\n\tchar *data_dirs = getenv(\"XDG_DATA_DIRS\");\n\tif (!(data_dirs && *data_dirs)) {\n\t\tdata_dirs = \"/usr/local/share:/usr/share\";\n\t}\n\tdata_dirs = strdup(data_dirs);\n\tchar *dir = strtok(data_dirs, \":\");\n\tdo {\n\t\tchar *path = format_str(\"%s/icons\", dir);\n\t\tlist_add(basedirs, path);\n\t} while ((dir = strtok(NULL, \":\")));\n\tfree(data_dirs);\n\n\tlist_t *basedirs_expanded = create_list();\n\tfor (int i = 0; i < basedirs->length; ++i) {\n\t\twordexp_t p;\n\t\tif (wordexp(basedirs->items[i], &p, WRDE_UNDEF) == 0) {\n\t\t\tif (dir_exists(p.we_wordv[0])) {\n\t\t\t\tlist_add(basedirs_expanded, strdup(p.we_wordv[0]));\n\t\t\t}\n\t\t\twordfree(&p);\n\t\t}\n\t}\n\n\tlist_free_items_and_destroy(basedirs);\n\n\treturn basedirs_expanded;\n}\n\nstatic void destroy_theme(struct icon_theme *theme) {\n\tif (!theme) {\n\t\treturn;\n\t}\n\tfree(theme->name);\n\tfree(theme->comment);\n\tlist_free_items_and_destroy(theme->inherits);\n\tlist_free_items_and_destroy(theme->directories);\n\tfree(theme->dir);\n\n\tfor (int i = 0; i < theme->subdirs->length; ++i) {\n\t\tstruct icon_theme_subdir *subdir = theme->subdirs->items[i];\n\t\tfree(subdir->name);\n\t\tfree(subdir);\n\t}\n\tlist_free(theme->subdirs);\n\tfree(theme);\n}\n\nstatic const char *group_handler(char *old_group, char *new_group,\n\t\tstruct icon_theme *theme) {\n\tif (!old_group) {\n\t\treturn new_group && strcmp(new_group, \"Icon Theme\") == 0 ? NULL :\n\t\t\t\"first group must be 'Icon Theme'\";\n\t}\n\n\tif (strcmp(old_group, \"Icon Theme\") == 0) {\n\t\tif (!theme->name) {\n\t\t\treturn \"missing required key 'Name'\";\n\t\t} else if (!theme->comment) {\n\t\t\treturn \"missing required key 'Comment'\";\n\t\t} else if (!theme->directories) {\n\t\t\treturn \"missing required key 'Directories'\";\n\t\t} else {\n\t\t\tfor (char *c = theme->name; *c; ++c) {\n\t\t\t\tif (*c == ',' || *c == ' ') {\n\t\t\t\t\treturn \"malformed theme name\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (theme->subdirs->length == 0) { // skip\n\t\t\treturn NULL;\n\t\t}\n\n\t\tstruct icon_theme_subdir *subdir =\n\t\t\ttheme->subdirs->items[theme->subdirs->length - 1];\n\t\tif (!subdir->size) {\n\t\t\treturn \"missing required key 'Size'\";\n\t\t}\n\n\t\tswitch (subdir->type) {\n\t\tcase FIXED: subdir->max_size = subdir->min_size = subdir->size;\n\t\t\tbreak;\n\t\tcase SCALABLE: {\n\t\t\tif (!subdir->max_size) subdir->max_size = subdir->size;\n\t\t\tif (!subdir->min_size) subdir->min_size = subdir->size;\n\t\t\tbreak;\n\t\t}\n\t\tcase THRESHOLD:\n\t\t\tsubdir->max_size = subdir->size + subdir->threshold;\n\t\t\tsubdir->min_size = subdir->size - subdir->threshold;\n\t\t}\n\t}\n\n\tif (new_group && list_seq_find(theme->directories, cmp_id, new_group) != -1) {\n\t\tstruct icon_theme_subdir *subdir = calloc(1, sizeof(struct icon_theme_subdir));\n\t\tif (!subdir) {\n\t\t\treturn \"out of memory\";\n\t\t}\n\t\tsubdir->name = strdup(new_group);\n\t\tsubdir->threshold = 2;\n\t\tlist_add(theme->subdirs, subdir);\n\t}\n\n\treturn NULL;\n}\n\nstatic const char *entry_handler(char *group, char *key, char *value,\n\t\tstruct icon_theme *theme) {\n\tif (strcmp(group, \"Icon Theme\") == 0) {\n\t\tif (strcmp(key, \"Name\") == 0) {\n\t\t\ttheme->name = strdup(value);\n\t\t} else if (strcmp(key, \"Comment\") == 0) {\n\t\t\ttheme->comment = strdup(value);\n\t\t} else if (strcmp(key, \"Inherits\") == 0) {\n\t\t\ttheme->inherits = split_string(value, \",\");\n\t\t} else if (strcmp(key, \"Directories\") == 0) {\n\t\t\ttheme->directories = split_string(value, \",\");\n\t\t} // Ignored: ScaledDirectories, Hidden, Example\n\t} else {\n\t\tif (theme->subdirs->length == 0) { // skip\n\t\t\treturn NULL;\n\t\t}\n\n\t\tstruct icon_theme_subdir *subdir =\n\t\t\ttheme->subdirs->items[theme->subdirs->length - 1];\n\t\tif (strcmp(subdir->name, group) != 0) { // skip\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (strcmp(key, \"Context\") == 0) {\n\t\t\treturn NULL; // ignored, but explicitly handled to not fail parsing\n\t\t} else if (strcmp(key, \"Type\") == 0) {\n\t\t\tif (strcmp(value, \"Fixed\") == 0) {\n\t\t\t\tsubdir->type = FIXED;\n\t\t\t} else if (strcmp(value, \"Scalable\") == 0) {\n\t\t\t\tsubdir->type = SCALABLE;\n\t\t\t} else if (strcmp(value, \"Threshold\") == 0) {\n\t\t\t\tsubdir->type = THRESHOLD;\n\t\t\t} else {\n\t\t\t\treturn \"invalid value - expected 'Fixed', 'Scalable' or 'Threshold'\";\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\n\t\tchar *end;\n\t\tint n = strtol(value, &end, 10);\n\t\tif (*end != '\\0') {\n\t\t\treturn \"invalid value - expected a number\";\n\t\t}\n\n\t\tif (strcmp(key, \"Size\") == 0) {\n\t\t\tsubdir->size = n;\n\t\t} else if (strcmp(key, \"MaxSize\") == 0) {\n\t\t\tsubdir->max_size = n;\n\t\t} else if (strcmp(key, \"MinSize\") == 0) {\n\t\t\tsubdir->min_size = n;\n\t\t} else if (strcmp(key, \"Threshold\") == 0) {\n\t\t\tsubdir->threshold = n;\n\t\t} // Ignored: Scale\n\t}\n\treturn NULL;\n}\n\n/*\n * This is a Freedesktop Desktop Entry parser (essentially INI)\n * It calls entry_handler for every entry\n * and group_handler between every group (as well as at both ends)\n * Handlers return whether an error occurred, which stops parsing\n */\nstatic struct icon_theme *read_theme_file(char *basedir, char *theme_name) {\n\t// look for index.theme file\n\tchar *path = format_str(\"%s/%s/index.theme\", basedir, theme_name);\n\tFILE *theme_file = fopen(path, \"r\");\n\tfree(path);\n\tif (!theme_file) {\n\t\treturn NULL;\n\t}\n\n\tstruct icon_theme *theme = calloc(1, sizeof(struct icon_theme));\n\tif (!theme) {\n\t\tfclose(theme_file);\n\t\treturn NULL;\n\t}\n\ttheme->subdirs = create_list();\n\n\tlist_t *groups = create_list();\n\n\tconst char *error = NULL;\n\tint line_no = 0;\n\tchar *full_line = NULL;\n\tsize_t full_len = 0;\n\tssize_t nread;\n\twhile ((nread = getline(&full_line, &full_len, theme_file)) != -1) {\n\t\t++line_no;\n\n\t\tchar *line = full_line - 1;\n\t\twhile (isspace(*++line)) {} // remove leading whitespace\n\t\tif (!*line || line[0] == '#') continue; // ignore blank lines & comments\n\n\t\tint len = nread - (line - full_line);\n\t\twhile (isspace(line[--len])) {}\n\t\tline[++len] = '\\0'; // remove trailing whitespace\n\n\t\tif (line[0] == '[') { // group header\n\t\t\t// check well-formed\n\t\t\tint i = 1;\n\t\t\tfor (; !iscntrl(line[i]) && line[i] != '[' && line[i] != ']'; ++i) {}\n\t\t\tif (i != --len || line[i] != ']') {\n\t\t\t\terror = \"malformed group header\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tline[len] = '\\0';\n\n\t\t\t// check group is not duplicate\n\t\t\tif (list_seq_find(groups, cmp_id, &line[1]) != -1) {\n\t\t\t\terror = \"duplicate group\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// call handler\n\t\t\tchar *last_group = groups->length > 0 ? groups->items[groups->length - 1] : NULL;\n\t\t\terror = group_handler(last_group, &line[1], theme);\n\t\t\tif (error) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlist_add(groups, strdup(&line[1]));\n\t\t} else { // key-value pair\n\t\t\tif (groups->length == 0) {\n\t\t\t\terror = \"unexpected content before first header\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// check well-formed\n\t\t\tint eok = 0;\n\t\t\tfor (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale?\n\t\t\tint i = eok - 1;\n\t\t\twhile (isspace(line[++i])) {}\n\t\t\tif (line[i] != '=') {\n\t\t\t\terror = \"malformed key-value pair\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tline[eok] = '\\0'; // split into key-value pair\n\t\t\tchar *value = &line[i];\n\t\t\twhile (isspace(*++value)) {}\n\t\t\t// TODO unescape value\n\n\t\t\terror = entry_handler(groups->items[groups->length - 1], line,\n\t\t\t\t\tvalue, theme);\n\t\t\tif (error) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!error) {\n\t\tif (groups->length > 0) {\n\t\t\terror = group_handler(groups->items[groups->length - 1], NULL, theme);\n\t\t} else {\n\t\t\terror = \"empty file\";\n\t\t}\n\t}\n\n\tif (!error) {\n\t\ttheme->dir = strdup(theme_name);\n\t} else {\n\t\tchar *last_group = groups->length > 0 ? groups->items[groups->length-1] : \"n/a\";\n\t\tsway_log(SWAY_DEBUG, \"Failed to load theme '%s' - parsing of file \"\n\t\t\t\t\"'%s/%s/index.theme' failed on line %d (group '%s'): %s\",\n\t\t\t\ttheme_name, basedir, theme_name, line_no, last_group, error);\n\t\tdestroy_theme(theme);\n\t\ttheme = NULL;\n\t}\n\n\tfree(full_line);\n\tlist_free_items_and_destroy(groups);\n\tfclose(theme_file);\n\treturn theme;\n}\n\nstatic list_t *load_themes_in_dir(char *basedir) {\n\tDIR *dir;\n\tif (!(dir = opendir(basedir))) {\n\t\treturn NULL;\n\t}\n\n\tlist_t *themes = create_list();\n\tstruct dirent *entry;\n\twhile ((entry = readdir(dir))) {\n\t\tif (entry->d_name[0] == '.') continue;\n\n\t\tstruct icon_theme *theme = read_theme_file(basedir, entry->d_name);\n\t\tif (theme) {\n\t\t\tlist_add(themes, theme);\n\t\t}\n\t}\n\tclosedir(dir);\n\treturn themes;\n}\n\nstatic void log_loaded_themes(list_t *themes) {\n\tif (themes->length == 0) {\n\t\tsway_log(SWAY_INFO, \"Warning: no icon themes loaded\");\n\t\treturn;\n\t}\n\n\tconst char sep[] = \", \";\n\tsize_t sep_len = strlen(sep);\n\n\tsize_t len = 0;\n\tfor (int i = 0; i < themes->length; ++i) {\n\t\tstruct icon_theme *theme = themes->items[i];\n\t\tlen += strlen(theme->name) + sep_len;\n\t}\n\n\tchar *str = malloc(len + 1);\n\tif (!str) {\n\t\treturn;\n\t}\n\tchar *p = str;\n\tfor (int i = 0; i < themes->length; ++i) {\n\t\tif (i > 0) {\n\t\t\tmemcpy(p, sep, sep_len);\n\t\t\tp += sep_len;\n\t\t}\n\n\t\tstruct icon_theme *theme = themes->items[i];\n\t\tsize_t name_len = strlen(theme->name);\n\t\tmemcpy(p, theme->name, name_len);\n\t\tp += name_len;\n\t}\n\t*p = '\\0';\n\n\tsway_log(SWAY_DEBUG, \"Loaded icon themes: %s\", str);\n\tfree(str);\n}\n\nvoid init_themes(list_t **themes, list_t **basedirs) {\n\t*basedirs = get_basedirs();\n\n\t*themes = create_list();\n\tfor (int i = 0; i < (*basedirs)->length; ++i) {\n\t\tlist_t *dir_themes = load_themes_in_dir((*basedirs)->items[i]);\n\t\tif (dir_themes == NULL) {\n\t\t\tcontinue;\n\t\t}\n\t\tlist_cat(*themes, dir_themes);\n\t\tlist_free(dir_themes);\n\t}\n\n\tlog_loaded_themes(*themes);\n}\n\nvoid finish_themes(list_t *themes, list_t *basedirs) {\n\tfor (int i = 0; i < themes->length; ++i) {\n\t\tdestroy_theme(themes->items[i]);\n\t}\n\tlist_free(themes);\n\tlist_free_items_and_destroy(basedirs);\n}\n\nstatic char *find_icon_in_subdir(char *name, char *basedir, char *theme,\n\t\tchar *subdir) {\n\tstatic const char *extensions[] = {\n#if HAVE_GDK_PIXBUF\n\t\t\"svg\",\n#endif\n\t\t\"png\",\n#if HAVE_GDK_PIXBUF\n\t\t\"xpm\" // deprecated\n#endif\n\t};\n\n\tfor (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) {\n\t\tchar *path = format_str(\"%s/%s/%s/%s.%s\",\n\t\t\tbasedir, theme, subdir, name, extensions[i]);\n\t\tif (access(path, R_OK) == 0) {\n\t\t\treturn path;\n\t\t}\n\t\tfree(path);\n\t}\n\n\treturn NULL;\n}\n\nstatic bool theme_exists_in_basedir(char *theme, char *basedir) {\n\tchar *path = format_str(\"%s/%s\", basedir, theme);\n\tbool ret = dir_exists(path);\n\tfree(path);\n\treturn ret;\n}\n\nstatic char *find_icon_with_theme(list_t *basedirs, list_t *themes, char *name,\n\t\tint size, char *theme_name, int *min_size, int *max_size) {\n\tstruct icon_theme *theme = NULL;\n\tfor (int i = 0; i < themes->length; ++i) {\n\t\ttheme = themes->items[i];\n\t\tif (strcmp(theme->name, theme_name) == 0) {\n\t\t\tbreak;\n\t\t}\n\t\ttheme = NULL;\n\t}\n\tif (!theme) return NULL;\n\n\tchar *icon = NULL;\n\tfor (int i = 0; i < basedirs->length; ++i) {\n\t\tif (!theme_exists_in_basedir(theme->dir, basedirs->items[i])) {\n\t\t\tcontinue;\n\t\t}\n\t\t// search backwards to hopefully hit scalable/larger icons first\n\t\tfor (int j = theme->subdirs->length - 1; j >= 0; --j) {\n\t\t\tstruct icon_theme_subdir *subdir = theme->subdirs->items[j];\n\t\t\tif (size >= subdir->min_size && size <= subdir->max_size) {\n\t\t\t\tif ((icon = find_icon_in_subdir(name, basedirs->items[i],\n\t\t\t\t\t\t\t\ttheme->dir, subdir->name))) {\n\t\t\t\t\t*min_size = subdir->min_size;\n\t\t\t\t\t*max_size = subdir->max_size;\n\t\t\t\t\treturn icon;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// inexact match\n\tunsigned smallest_error = -1; // UINT_MAX\n\tfor (int i = 0; i < basedirs->length; ++i) {\n\t\tif (!theme_exists_in_basedir(theme->dir, basedirs->items[i])) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (int j = theme->subdirs->length - 1; j >= 0; --j) {\n\t\t\tstruct icon_theme_subdir *subdir = theme->subdirs->items[j];\n\t\t\tunsigned error = (size > subdir->max_size ? size - subdir->max_size : 0)\n\t\t\t\t+ (size < subdir->min_size ? subdir->min_size - size : 0);\n\t\t\tif (error < smallest_error) {\n\t\t\t\tchar *test_icon = find_icon_in_subdir(name, basedirs->items[i],\n\t\t\t\t\t\ttheme->dir, subdir->name);\n\t\t\t\tif (test_icon) {\n\t\t\t\t\ticon = test_icon;\n\t\t\t\t\tsmallest_error = error;\n\t\t\t\t\t*min_size = subdir->min_size;\n\t\t\t\t\t*max_size = subdir->max_size;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!icon && theme->inherits) {\n\t\tfor (int i = 0; i < theme->inherits->length; ++i) {\n\t\t\ticon = find_icon_with_theme(basedirs, themes, name, size,\n\t\t\t\t\ttheme->inherits->items[i], min_size, max_size);\n\t\t\tif (icon) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn icon;\n}\n\nstatic char *find_fallback_icon(list_t *basedirs, char *name, int *min_size,\n\t\tint *max_size) {\n\tfor (int i = 0; i < basedirs->length; ++i) {\n\t\tchar *icon = find_icon_in_subdir(name, basedirs->items[i], \"\", \"\");\n\t\tif (icon) {\n\t\t\t*min_size = 1;\n\t\t\t*max_size = 512;\n\t\t\treturn icon;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nchar *find_icon(list_t *themes, list_t *basedirs, char *name, int size,\n\t\tchar *theme, int *min_size, int *max_size) {\n\t// TODO https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#implementation_notes\n\tchar *icon = NULL;\n\tif (theme) {\n\t\ticon = find_icon_with_theme(basedirs, themes, name, size, theme,\n\t\t\t\tmin_size, max_size);\n\t}\n\tif (!icon && !(theme && strcmp(theme, \"Hicolor\") == 0)) {\n\t\ticon = find_icon_with_theme(basedirs, themes, name, size, \"Hicolor\",\n\t\t\t\tmin_size, max_size);\n\t}\n\tif (!icon) {\n\t\ticon = find_fallback_icon(basedirs, name, min_size, max_size);\n\t}\n\treturn icon;\n}\n"
  },
  {
    "path": "swaybar/tray/item.c",
    "content": "#include <arpa/inet.h>\n#include <cairo.h>\n#include <limits.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"swaybar/bar.h\"\n#include \"swaybar/config.h\"\n#include \"swaybar/image.h\"\n#include \"swaybar/input.h\"\n#include \"swaybar/tray/host.h\"\n#include \"swaybar/tray/icon.h\"\n#include \"swaybar/tray/item.h\"\n#include \"swaybar/tray/tray.h\"\n#include \"cairo_util.h\"\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n\n// TODO menu\n\nstatic bool sni_ready(struct swaybar_sni *sni) {\n\treturn sni->status && (sni->status[0] == 'N' ? // NeedsAttention\n\t\t\tsni->attention_icon_name || sni->attention_icon_pixmap :\n\t\t\tsni->icon_name || sni->icon_pixmap);\n}\n\nstatic void set_sni_dirty(struct swaybar_sni *sni) {\n\tif (sni_ready(sni)) {\n\t\tsni->target_size = sni->min_size = sni->max_size = 0; // invalidate previous icon\n\t\tset_bar_dirty(sni->tray->bar);\n\t}\n}\n\nstatic int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni,\n\t\tconst char *prop, list_t **dest) {\n\tint ret = sd_bus_message_enter_container(msg, 'a', \"(iiay)\");\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"%s %s: %s\", sni->watcher_id, prop, strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tif (sd_bus_message_at_end(msg, 0)) {\n\t\tsway_log(SWAY_DEBUG, \"%s %s no. of icons = 0\", sni->watcher_id, prop);\n\t\treturn ret;\n\t}\n\n\tlist_t *pixmaps = create_list();\n\tif (!pixmaps) {\n\t\treturn -12; // -ENOMEM\n\t}\n\n\twhile (!sd_bus_message_at_end(msg, 0)) {\n\t\tret = sd_bus_message_enter_container(msg, 'r', \"iiay\");\n\t\tif (ret < 0) {\n\t\t\tsway_log(SWAY_ERROR, \"%s %s: %s\", sni->watcher_id, prop, strerror(-ret));\n\t\t\tgoto error;\n\t\t}\n\n\t\tint width, height;\n\t\tret = sd_bus_message_read(msg, \"ii\", &width, &height);\n\t\tif (ret < 0) {\n\t\t\tsway_log(SWAY_ERROR, \"%s %s: %s\", sni->watcher_id, prop, strerror(-ret));\n\t\t\tgoto error;\n\t\t}\n\n\t\tconst void *pixels;\n\t\tsize_t npixels;\n\t\tret = sd_bus_message_read_array(msg, 'y', &pixels, &npixels);\n\t\tif (ret < 0) {\n\t\t\tsway_log(SWAY_ERROR, \"%s %s: %s\", sni->watcher_id, prop, strerror(-ret));\n\t\t\tgoto error;\n\t\t}\n\n\t\tif (height > 0 && width == height) {\n\t\t\tsway_log(SWAY_DEBUG, \"%s %s: found icon w:%d h:%d\", sni->watcher_id, prop, width, height);\n\t\t\tstruct swaybar_pixmap *pixmap =\n\t\t\t\tmalloc(sizeof(struct swaybar_pixmap) + npixels);\n\t\t\tpixmap->size = height;\n\n\t\t\t// convert from network byte order to host byte order\n\t\t\tfor (int i = 0; i < height * width; ++i) {\n\t\t\t\t((uint32_t *)pixmap->pixels)[i] = ntohl(((uint32_t *)pixels)[i]);\n\t\t\t}\n\n\t\t\tlist_add(pixmaps, pixmap);\n\t\t} else {\n\t\t\tsway_log(SWAY_DEBUG, \"%s %s: discard invalid icon w:%d h:%d\", sni->watcher_id, prop, width, height);\n\t\t}\n\n\t\tsd_bus_message_exit_container(msg);\n\t}\n\n\tif (pixmaps->length < 1) {\n\t\tsway_log(SWAY_DEBUG, \"%s %s no. of icons = 0\", sni->watcher_id, prop);\n\t\tgoto error;\n\t}\n\n\tlist_free_items_and_destroy(*dest);\n\t*dest = pixmaps;\n\tsway_log(SWAY_DEBUG, \"%s %s no. of icons = %d\", sni->watcher_id, prop,\n\t\t\tpixmaps->length);\n\n\treturn ret;\nerror:\n\tlist_free_items_and_destroy(pixmaps);\n\treturn ret;\n}\n\nstatic int get_property_callback(sd_bus_message *msg, void *data,\n\t\tsd_bus_error *error) {\n\tstruct swaybar_sni_slot *d = data;\n\tstruct swaybar_sni *sni = d->sni;\n\tconst char *prop = d->prop;\n\tconst char *type = d->type;\n\tvoid *dest = d->dest;\n\n\tint ret;\n\tif (sd_bus_message_is_method_error(msg, NULL)) {\n\t\tconst sd_bus_error *err = sd_bus_message_get_error(msg);\n\t\tsway_log_importance_t log_lv = SWAY_ERROR;\n\t\tif ((!strcmp(prop, \"IconThemePath\")) &&\n\t\t\t\t(!strcmp(err->name, SD_BUS_ERROR_UNKNOWN_PROPERTY))) {\n\t\t\tlog_lv = SWAY_DEBUG;\n\t\t}\n\t\tsway_log(log_lv, \"%s %s: %s\", sni->watcher_id, prop, err->message);\n\t\tret = sd_bus_message_get_errno(msg);\n\t\tgoto cleanup;\n\t}\n\n\tret = sd_bus_message_enter_container(msg, 'v', type);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"%s %s: %s\", sni->watcher_id, prop, strerror(-ret));\n\t\tgoto cleanup;\n\t}\n\n\tif (!type) {\n\t\tret = read_pixmap(msg, sni, prop, dest);\n\t\tif (ret < 0) {\n\t\t\tgoto cleanup;\n\t\t}\n\t} else {\n\t\tif (*type == 's' || *type == 'o') {\n\t\t\tfree(*(char **)dest);\n\t\t}\n\n\t\tret = sd_bus_message_read(msg, type, dest);\n\t\tif (ret < 0) {\n\t\t\tsway_log(SWAY_ERROR, \"%s %s: %s\", sni->watcher_id, prop, strerror(-ret));\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\tif (*type == 's' || *type == 'o') {\n\t\t\tchar **str = dest;\n\t\t\t*str = strdup(*str);\n\t\t\tsway_log(SWAY_DEBUG, \"%s %s = '%s'\", sni->watcher_id, prop, *str);\n\t\t} else if (*type == 'b') {\n\t\t\tsway_log(SWAY_DEBUG, \"%s %s = %s\", sni->watcher_id, prop,\n\t\t\t\t\t*(bool *)dest ? \"true\" : \"false\");\n\t\t}\n\t}\n\n\tif (strcmp(prop, \"Status\") == 0 || (sni->status && (sni->status[0] == 'N' ?\n\t\t\t\tprop[0] == 'A' : has_prefix(prop, \"Icon\")))) {\n\t\tset_sni_dirty(sni);\n\t}\ncleanup:\n\twl_list_remove(&d->link);\n\tfree(data);\n\treturn ret;\n}\n\nstatic void sni_get_property_async(struct swaybar_sni *sni, const char *prop,\n\t\tconst char *type, void *dest) {\n\tstruct swaybar_sni_slot *data = calloc(1, sizeof(struct swaybar_sni_slot));\n\tdata->sni = sni;\n\tdata->prop = prop;\n\tdata->type = type;\n\tdata->dest = dest;\n\tint ret = sd_bus_call_method_async(sni->tray->bus, &data->slot, sni->service,\n\t\t\tsni->path, \"org.freedesktop.DBus.Properties\", \"Get\",\n\t\t\tget_property_callback, data, \"ss\", sni->interface, prop);\n\tif (ret >= 0) {\n\t\twl_list_insert(&sni->slots, &data->link);\n\t} else {\n\t\tsway_log(SWAY_ERROR, \"%s %s: %s\", sni->watcher_id, prop, strerror(-ret));\n\t\tfree(data);\n\t}\n}\n\n/*\n * There is a quirk in sd-bus that in some systems, it is unable to get the\n * well-known names on the bus, so it cannot identify if an incoming signal,\n * which uses the sender's unique name, actually matches the callback's matching\n * sender if the callback uses a well-known name, in which case it just calls\n * the callback and hopes for the best, resulting in false positives. In the\n * case of NewIcon & NewAttentionIcon, this doesn't affect anything, but it\n * means that for NewStatus, if the SNI does not definitely match the sender,\n * then the safe thing to do is to query the status independently.\n * This function returns 1 if the SNI definitely matches the signal sender,\n * which is returned by the calling function to indicate that signal matching\n * can stop since it has already found the required callback, otherwise, it\n * returns 0, which allows matching to continue.\n */\nstatic int sni_check_msg_sender(struct swaybar_sni *sni, sd_bus_message *msg,\n\t\tconst char *signal) {\n\tbool has_well_known_names =\n\t\tsd_bus_creds_get_mask(sd_bus_message_get_creds(msg)) & SD_BUS_CREDS_WELL_KNOWN_NAMES;\n\tif (sni->service[0] == ':' || has_well_known_names) {\n\t\tsway_log(SWAY_DEBUG, \"%s has new %s\", sni->watcher_id, signal);\n\t\treturn 1;\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"%s may have new %s\", sni->watcher_id, signal);\n\t\treturn 0;\n\t}\n}\n\nstatic int handle_new_icon(sd_bus_message *msg, void *data, sd_bus_error *error) {\n\tstruct swaybar_sni *sni = data;\n\tsni_get_property_async(sni, \"IconName\", \"s\", &sni->icon_name);\n\tsni_get_property_async(sni, \"IconPixmap\", NULL, &sni->icon_pixmap);\n\tif (!strcmp(sni->interface, \"org.kde.StatusNotifierItem\")) {\n\t\tsni_get_property_async(sni, \"IconThemePath\", \"s\", &sni->icon_theme_path);\n\t}\n\treturn sni_check_msg_sender(sni, msg, \"icon\");\n}\n\nstatic int handle_new_attention_icon(sd_bus_message *msg, void *data,\n\t\tsd_bus_error *error) {\n\tstruct swaybar_sni *sni = data;\n\tsni_get_property_async(sni, \"AttentionIconName\", \"s\", &sni->attention_icon_name);\n\tsni_get_property_async(sni, \"AttentionIconPixmap\", NULL, &sni->attention_icon_pixmap);\n\treturn sni_check_msg_sender(sni, msg, \"attention icon\");\n}\n\nstatic int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *error) {\n\tstruct swaybar_sni *sni = data;\n\tint ret = sni_check_msg_sender(sni, msg, \"status\");\n\tif (ret == 1) {\n\t\tchar *status;\n\t\tint r = sd_bus_message_read(msg, \"s\", &status);\n\t\tif (r < 0) {\n\t\t\tsway_log(SWAY_ERROR, \"%s new status error: %s\", sni->watcher_id, strerror(-ret));\n\t\t\tret = r;\n\t\t} else {\n\t\t\tfree(sni->status);\n\t\t\tsni->status = strdup(status);\n\t\t\tsway_log(SWAY_DEBUG, \"%s has new status = '%s'\", sni->watcher_id, status);\n\t\t\tset_sni_dirty(sni);\n\t\t}\n\t} else {\n\t\tsni_get_property_async(sni, \"Status\", \"s\", &sni->status);\n\t}\n\n\treturn ret;\n}\n\nstatic void sni_match_signal_async(struct swaybar_sni *sni, char *signal,\n\t\tsd_bus_message_handler_t callback) {\n\tstruct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot));\n\tint ret = sd_bus_match_signal_async(sni->tray->bus, &slot->slot,\n\t\t\tsni->service, sni->path, sni->interface, signal, callback, NULL, sni);\n\tif (ret >= 0) {\n\t\twl_list_insert(&sni->slots, &slot->link);\n\t} else {\n\t\tsway_log(SWAY_ERROR, \"%s failed to subscribe to signal %s: %s\",\n\t\t\t\tsni->service, signal, strerror(-ret));\n\t\tfree(slot);\n\t}\n}\n\nstruct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) {\n\tstruct swaybar_sni *sni = calloc(1, sizeof(struct swaybar_sni));\n\tif (!sni) {\n\t\treturn NULL;\n\t}\n\tsni->tray = tray;\n\twl_list_init(&sni->slots);\n\tsni->watcher_id = strdup(id);\n\tchar *path_ptr = strchr(id, '/');\n\tif (!path_ptr) {\n\t\tsni->service = strdup(id);\n\t\tsni->path = strdup(\"/StatusNotifierItem\");\n\t\tsni->interface = \"org.freedesktop.StatusNotifierItem\";\n\t} else {\n\t\tsni->service = strndup(id, path_ptr - id);\n\t\tsni->path = strdup(path_ptr);\n\t\tsni->interface = \"org.kde.StatusNotifierItem\";\n\t\tsni_get_property_async(sni, \"IconThemePath\", \"s\", &sni->icon_theme_path);\n\t}\n\n\t// Ignored: Category, Id, Title, WindowId, OverlayIconName,\n\t//          OverlayIconPixmap, AttentionMovieName, ToolTip\n\tsni_get_property_async(sni, \"Status\", \"s\", &sni->status);\n\tsni_get_property_async(sni, \"IconName\", \"s\", &sni->icon_name);\n\tsni_get_property_async(sni, \"IconPixmap\", NULL, &sni->icon_pixmap);\n\tsni_get_property_async(sni, \"AttentionIconName\", \"s\", &sni->attention_icon_name);\n\tsni_get_property_async(sni, \"AttentionIconPixmap\", NULL, &sni->attention_icon_pixmap);\n\tsni_get_property_async(sni, \"ItemIsMenu\", \"b\", &sni->item_is_menu);\n\tsni_get_property_async(sni, \"Menu\", \"o\", &sni->menu);\n\n\tsni_match_signal_async(sni, \"NewIcon\", handle_new_icon);\n\tsni_match_signal_async(sni, \"NewAttentionIcon\", handle_new_attention_icon);\n\tsni_match_signal_async(sni, \"NewStatus\", handle_new_status);\n\n\treturn sni;\n}\n\nvoid destroy_sni(struct swaybar_sni *sni) {\n\tif (!sni) {\n\t\treturn;\n\t}\n\n\tcairo_surface_destroy(sni->icon);\n\tfree(sni->watcher_id);\n\tfree(sni->service);\n\tfree(sni->path);\n\tfree(sni->status);\n\tfree(sni->icon_name);\n\tlist_free_items_and_destroy(sni->icon_pixmap);\n\tfree(sni->attention_icon_name);\n\tlist_free_items_and_destroy(sni->attention_icon_pixmap);\n\tfree(sni->menu);\n\tfree(sni->icon_theme_path);\n\n\tstruct swaybar_sni_slot *slot, *slot_tmp;\n\twl_list_for_each_safe(slot, slot_tmp, &sni->slots, link) {\n\t\tsd_bus_slot_unref(slot->slot);\n\t\tfree(slot);\n\t}\n\n\tfree(sni);\n}\n\nstatic void handle_click(struct swaybar_sni *sni, int x, int y,\n\t\tuint32_t button, int delta) {\n\tconst char *method = NULL;\n\tstruct tray_binding *binding = NULL;\n\twl_list_for_each(binding, &sni->tray->bar->config->tray_bindings, link) {\n\t\tif (binding->button == button) {\n\t\t\tmethod = binding->command;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!method) {\n\t\tstatic const char *default_bindings[10] = {\n\t\t\t\"nop\",\n\t\t\t\"Activate\",\n\t\t\t\"SecondaryActivate\",\n\t\t\t\"ContextMenu\",\n\t\t\t\"ScrollUp\",\n\t\t\t\"ScrollDown\",\n\t\t\t\"ScrollLeft\",\n\t\t\t\"ScrollRight\",\n\t\t\t\"nop\",\n\t\t\t\"nop\"\n\t\t};\n\t\tmethod = default_bindings[event_to_x11_button(button)];\n\t}\n\tif (strcmp(method, \"nop\") == 0) {\n\t\treturn;\n\t}\n\tif (sni->item_is_menu && strcmp(method, \"Activate\") == 0) {\n\t\tmethod = \"ContextMenu\";\n\t}\n\n\tif (has_prefix(method, \"Scroll\")) {\n\t\tchar dir = method[strlen(\"Scroll\")];\n\t\tchar *orientation = (dir == 'U' || dir == 'D') ? \"vertical\" : \"horizontal\";\n\t\tint sign = (dir == 'U' || dir == 'L') ? -1 : 1;\n\n\t\tsd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->path,\n\t\t\t\tsni->interface, \"Scroll\", NULL, NULL, \"is\", delta*sign, orientation);\n\t} else {\n\t\tsd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->path,\n\t\t\t\tsni->interface, method, NULL, NULL, \"ii\", x, y);\n\t}\n}\n\nstatic int cmp_sni_id(const void *item, const void *cmp_to) {\n\tconst struct swaybar_sni *sni = item;\n\treturn strcmp(sni->watcher_id, cmp_to);\n}\n\nstatic enum hotspot_event_handling icon_hotspot_callback(\n\t\tstruct swaybar_output *output, struct swaybar_hotspot *hotspot,\n\t\tdouble x, double y, uint32_t button, bool released, void *data) {\n\tsway_log(SWAY_DEBUG, \"Clicked on %s\", (char *)data);\n\n\tstruct swaybar_tray *tray = output->bar->tray;\n\tint idx = list_seq_find(tray->items, cmp_sni_id, data);\n\n\tif (idx != -1) {\n\t\tif (released) {\n\t\t\t// Since we handle the pressed event, also handle the released event\n\t\t\t// to block it from falling through to a binding in the bar\n\t\t\treturn HOTSPOT_IGNORE;\n\t\t}\n\t\tstruct swaybar_sni *sni = tray->items->items[idx];\n\t\t// guess global position since wayland doesn't expose it\n\t\tstruct swaybar_config *config = tray->bar->config;\n\t\tint global_x = output->output_x + config->gaps.left + x;\n\t\tbool top_bar = config->position & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;\n\t\tint global_y = output->output_y + (top_bar ? config->gaps.top + y:\n\t\t\t\t(int) output->output_height - config->gaps.bottom - y);\n\n\t\tsway_log(SWAY_DEBUG, \"Guessing click position at (%d, %d)\", global_x, global_y);\n\t\thandle_click(sni, global_x, global_y, button, 1); // TODO get delta from event\n\t\treturn HOTSPOT_IGNORE;\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"but it doesn't exist\");\n\t}\n\n\treturn HOTSPOT_PROCESS;\n}\n\nstatic void reload_sni(struct swaybar_sni *sni, char *icon_theme,\n\t\tint target_size) {\n\tchar *icon_name = sni->status[0] == 'N' ?\n\t\tsni->attention_icon_name : sni->icon_name;\n\tif (icon_name) {\n\t\tlist_t *icon_search_paths = create_list();\n\t\tlist_cat(icon_search_paths, sni->tray->basedirs);\n\t\tif (sni->icon_theme_path) {\n\t\t\tlist_add(icon_search_paths, sni->icon_theme_path);\n\t\t}\n\t\tchar *icon_path = find_icon(sni->tray->themes, icon_search_paths,\n\t\t\t\ticon_name, target_size, icon_theme,\n\t\t\t\t&sni->min_size, &sni->max_size);\n\t\tlist_free(icon_search_paths);\n\t\tif (icon_path) {\n\t\t\tcairo_surface_destroy(sni->icon);\n\t\t\tsni->icon = load_image(icon_path);\n\t\t\tfree(icon_path);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlist_t *pixmaps = sni->status[0] == 'N' ?\n\t\tsni->attention_icon_pixmap : sni->icon_pixmap;\n\tif (pixmaps) {\n\t\tstruct swaybar_pixmap *pixmap = NULL;\n\t\tint min_error = INT_MAX;\n\t\tfor (int i = 0; i < pixmaps->length; ++i) {\n\t\t\tstruct swaybar_pixmap *p = pixmaps->items[i];\n\t\t\tint e = abs(target_size - p->size);\n\t\t\tif (e < min_error) {\n\t\t\t\tpixmap = p;\n\t\t\t\tmin_error = e;\n\t\t\t}\n\t\t}\n\t\tcairo_surface_destroy(sni->icon);\n\t\tsni->icon = cairo_image_surface_create_for_data(pixmap->pixels,\n\t\t\t\tCAIRO_FORMAT_ARGB32, pixmap->size, pixmap->size,\n\t\t\t\tcairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixmap->size));\n\t}\n}\n\nuint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,\n\t\tstruct swaybar_sni *sni) {\n\tuint32_t height = output->height * output->scale;\n\tint padding = output->bar->config->tray_padding;\n\tint target_size = height - 2*padding;\n\tif (target_size != sni->target_size && sni_ready(sni)) {\n\t\t// check if another icon should be loaded\n\t\tif (target_size < sni->min_size || target_size > sni->max_size) {\n\t\t\treload_sni(sni, output->bar->config->icon_theme, target_size);\n\t\t}\n\n\t\tsni->target_size = target_size;\n\t}\n\n\t// Passive\n\tif (sni->status && sni->status[0] == 'P') {\n\t\treturn 0;\n\t}\n\n\tint icon_size;\n\tcairo_surface_t *icon;\n\tif (sni->icon) {\n\t\tint actual_size = cairo_image_surface_get_height(sni->icon);\n\t\ticon_size = actual_size < target_size ?\n\t\t\tactual_size*(target_size/actual_size) : target_size;\n\t\ticon = cairo_image_surface_scale(sni->icon, icon_size, icon_size);\n\t} else { // draw a :(\n\t\ticon_size = target_size*0.8;\n\t\ticon = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, icon_size, icon_size);\n\t\tcairo_t *cairo_icon = cairo_create(icon);\n\t\tcairo_set_source_u32(cairo_icon, 0xFF0000FF);\n\t\tcairo_translate(cairo_icon, icon_size/2, icon_size/2);\n\t\tcairo_scale(cairo_icon, icon_size/2, icon_size/2);\n\t\tcairo_arc(cairo_icon, 0, 0, 1, 0, 7);\n\t\tcairo_fill(cairo_icon);\n\t\tcairo_set_operator(cairo_icon, CAIRO_OPERATOR_CLEAR);\n\t\tcairo_arc(cairo_icon, 0.35, -0.3, 0.1, 0, 7);\n\t\tcairo_fill(cairo_icon);\n\t\tcairo_arc(cairo_icon, -0.35, -0.3, 0.1, 0, 7);\n\t\tcairo_fill(cairo_icon);\n\t\tcairo_arc(cairo_icon, 0, 0.75, 0.5, 3.71238898038469, 5.71238898038469);\n\t\tcairo_set_line_width(cairo_icon, 0.1);\n\t\tcairo_stroke(cairo_icon);\n\t\tcairo_destroy(cairo_icon);\n\t}\n\n\tdouble descaled_padding = (double)padding / output->scale;\n\tdouble descaled_icon_size = (double)icon_size / output->scale;\n\n\tint size = descaled_icon_size + 2 * descaled_padding;\n\t*x -= size;\n\tint icon_y = floor((output->height - size) / 2.0);\n\n\tcairo_operator_t op = cairo_get_operator(cairo);\n\tcairo_set_operator(cairo, CAIRO_OPERATOR_OVER);\n\n\tcairo_matrix_t scale_matrix;\n\tcairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon);\n\t// TODO: check cairo_pattern_status for \"ENOMEM\"\n\tcairo_matrix_init_scale(&scale_matrix, output->scale, output->scale);\n\tcairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding));\n\tcairo_pattern_set_matrix(icon_pattern, &scale_matrix);\n\tcairo_set_source(cairo, icon_pattern);\n\tcairo_rectangle(cairo, *x, icon_y, size, size);\n\tcairo_fill(cairo);\n\n\tcairo_set_operator(cairo, op);\n\n\tcairo_pattern_destroy(icon_pattern);\n\tcairo_surface_destroy(icon);\n\n\tstruct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));\n\thotspot->x = *x;\n\thotspot->y = 0;\n\thotspot->width = size;\n\thotspot->height = output->height;\n\thotspot->callback = icon_hotspot_callback;\n\thotspot->destroy = free;\n\thotspot->data = strdup(sni->watcher_id);\n\twl_list_insert(&output->hotspots, &hotspot->link);\n\n\treturn output->height;\n}\n"
  },
  {
    "path": "swaybar/tray/tray.c",
    "content": "#include <cairo.h>\n#include <poll.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"swaybar/config.h\"\n#include \"swaybar/bar.h\"\n#include \"swaybar/tray/icon.h\"\n#include \"swaybar/tray/host.h\"\n#include \"swaybar/tray/item.h\"\n#include \"swaybar/tray/tray.h\"\n#include \"swaybar/tray/watcher.h\"\n#include \"list.h\"\n#include \"log.h\"\n\nstatic int handle_lost_watcher(sd_bus_message *msg,\n\t\tvoid *data, sd_bus_error *error) {\n\tchar *service, *old_owner, *new_owner;\n\tint ret = sd_bus_message_read(msg, \"sss\", &service, &old_owner, &new_owner);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to parse owner change message: %s\", strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tif (!*new_owner) {\n\t\tstruct swaybar_tray *tray = data;\n\t\tif (strcmp(service, \"org.freedesktop.StatusNotifierWatcher\") == 0) {\n\t\t\ttray->watcher_xdg = create_watcher(\"freedesktop\", tray->bus);\n\t\t} else if (strcmp(service, \"org.kde.StatusNotifierWatcher\") == 0) {\n\t\t\ttray->watcher_kde = create_watcher(\"kde\", tray->bus);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstruct swaybar_tray *create_tray(struct swaybar *bar) {\n\tsway_log(SWAY_DEBUG, \"Initializing tray\");\n\n\tsd_bus *bus;\n\tint ret = sd_bus_open_user(&bus);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to connect to user bus: %s\", strerror(-ret));\n\t\treturn NULL;\n\t}\n\n\tstruct swaybar_tray *tray = calloc(1, sizeof(struct swaybar_tray));\n\tif (!tray) {\n\t\treturn NULL;\n\t}\n\ttray->bar = bar;\n\ttray->bus = bus;\n\ttray->fd = sd_bus_get_fd(tray->bus);\n\n\ttray->watcher_xdg = create_watcher(\"freedesktop\", tray->bus);\n\ttray->watcher_kde = create_watcher(\"kde\", tray->bus);\n\n\tret = sd_bus_match_signal(bus, NULL, \"org.freedesktop.DBus\",\n\t\t\t\"/org/freedesktop/DBus\", \"org.freedesktop.DBus\",\n\t\t\t\"NameOwnerChanged\", handle_lost_watcher, tray);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to subscribe to unregistering events: %s\",\n\t\t\t\tstrerror(-ret));\n\t}\n\n\ttray->items = create_list();\n\n\tinit_host(&tray->host_xdg, \"freedesktop\", tray);\n\tinit_host(&tray->host_kde, \"kde\", tray);\n\n\tinit_themes(&tray->themes, &tray->basedirs);\n\n\treturn tray;\n}\n\nvoid destroy_tray(struct swaybar_tray *tray) {\n\tif (!tray) {\n\t\treturn;\n\t}\n\tfinish_host(&tray->host_xdg);\n\tfinish_host(&tray->host_kde);\n\tfor (int i = 0; i < tray->items->length; ++i) {\n\t\tdestroy_sni(tray->items->items[i]);\n\t}\n\tlist_free(tray->items);\n\tdestroy_watcher(tray->watcher_xdg);\n\tdestroy_watcher(tray->watcher_kde);\n\tsd_bus_flush_close_unref(tray->bus);\n\tfinish_themes(tray->themes, tray->basedirs);\n\tfree(tray);\n}\n\nvoid tray_in(int fd, short mask, void *data) {\n\tstruct swaybar *bar = data;\n\tint ret;\n\n\tif (mask & (POLLHUP | POLLERR)) {\n        sway_log(SWAY_ERROR, \"D-Bus connection closed unexpectedly\");\n\t\tbar->running = false;\n\t\treturn;\n    }\n\n\twhile ((ret = sd_bus_process(bar->tray->bus, NULL)) > 0) {\n\t\t// This space intentionally left blank\n\t}\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to process bus: %s\", strerror(-ret));\n\t}\n}\n\nstatic int cmp_output(const void *item, const void *cmp_to) {\n\tconst struct swaybar_output *output = cmp_to;\n\tif (output->identifier && strcmp(item, output->identifier) == 0) {\n\t\treturn 0;\n\t}\n\treturn strcmp(item, output->name);\n}\n\nuint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) {\n\tstruct swaybar_config *config = output->bar->config;\n\tif (config->tray_outputs) {\n\t\tif (list_seq_find(config->tray_outputs, cmp_output, output) == -1) {\n\t\t\treturn 0;\n\t\t}\n\t} // else display on all\n\n\tif ((int)(output->height * output->scale) <= 2 * config->tray_padding) {\n\t\treturn (2 * config->tray_padding + 1) / output->scale;\n\t}\n\n\tuint32_t max_height = 0;\n\tstruct swaybar_tray *tray = output->bar->tray;\n\tfor (int i = 0; i < tray->items->length; ++i) {\n\t\tuint32_t h = render_sni(cairo, output, x, tray->items->items[i]);\n\t\tif (h > max_height) {\n\t\t\tmax_height = h;\n\t\t}\n\t}\n\n\treturn max_height;\n}\n"
  },
  {
    "path": "swaybar/tray/watcher.c",
    "content": "#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"list.h\"\n#include \"log.h\"\n#include \"stringop.h\"\n#include \"swaybar/tray/watcher.h\"\n\nstatic const char *obj_path = \"/StatusNotifierWatcher\";\n\nstatic bool using_standard_protocol(struct swaybar_watcher *watcher) {\n\treturn watcher->interface[strlen(\"org.\")] == 'f'; // freedesktop\n}\n\nstatic int cmp_id(const void *item, const void *cmp_to) {\n\treturn strcmp(item, cmp_to);\n}\n\nstatic int handle_lost_service(sd_bus_message *msg,\n\t\tvoid *data, sd_bus_error *error) {\n\tchar *service, *old_owner, *new_owner;\n\tint ret = sd_bus_message_read(msg, \"sss\", &service, &old_owner, &new_owner);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to parse owner change message: %s\", strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tif (!*new_owner) {\n\t\tstruct swaybar_watcher *watcher = data;\n\t\tfor (int idx = 0; idx < watcher->items->length; ++idx) {\n\t\t\tchar *id = watcher->items->items[idx];\n\t\t\tbool cmp_res = using_standard_protocol(watcher) ?\n\t\t\t\tcmp_id(id, service) == 0 : has_prefix(id, service);\n\t\t\tif (cmp_res) {\n\t\t\t\tsway_log(SWAY_DEBUG, \"Unregistering Status Notifier Item '%s'\", id);\n\t\t\t\tlist_del(watcher->items, idx--);\n\t\t\t\tsd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,\n\t\t\t\t\t\t\"StatusNotifierItemUnregistered\", \"s\", id);\n\t\t\t\tsd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,\n\t\t\t\t\t\t\"RegisteredStatusNotifierItems\", NULL);\n\t\t\t\tfree(id);\n\t\t\t\tif (using_standard_protocol(watcher)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tint idx = list_seq_find(watcher->hosts, cmp_id, service);\n\t\tif (idx != -1) {\n\t\t\tsway_log(SWAY_DEBUG, \"Unregistering Status Notifier Host '%s'\", service);\n\t\t\tfree(watcher->hosts->items[idx]);\n\t\t\tlist_del(watcher->hosts, idx);\n\t\t\tif (watcher->hosts->length == 0) {\n\t\t\t\tsd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,\n\t\t\t\t\t\t\"IsStatusNotifierHostRegistered\", NULL);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstatic int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) {\n\tchar *service_or_path, *id;\n\tint ret = sd_bus_message_read(msg, \"s\", &service_or_path);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to parse register SNI message: %s\", strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tstruct swaybar_watcher *watcher = data;\n\tif (using_standard_protocol(watcher)) {\n\t\tid = strdup(service_or_path);\n\t} else {\n\t\tconst char *service, *path;\n\t\tif (service_or_path[0] == '/') {\n\t\t\tservice = sd_bus_message_get_sender(msg);\n\t\t\tpath = service_or_path;\n\t\t} else {\n\t\t\tservice = service_or_path;\n\t\t\tpath = \"/StatusNotifierItem\";\n\t\t}\n\t\tid = format_str(\"%s%s\", service, path);\n\t}\n\n\tif (list_seq_find(watcher->items, cmp_id, id) == -1) {\n\t\tsway_log(SWAY_DEBUG, \"Registering Status Notifier Item '%s'\", id);\n\t\tlist_add(watcher->items, id);\n\t\tsd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,\n\t\t\t\t\"RegisteredStatusNotifierItems\", NULL);\n\t\tsd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,\n\t\t\t\t\"StatusNotifierItemRegistered\", \"s\", id);\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Status Notifier Item '%s' already registered\", id);\n\t\tfree(id);\n\t}\n\n\treturn sd_bus_reply_method_return(msg, \"\");\n}\n\nstatic int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) {\n\tchar *service;\n\tint ret = sd_bus_message_read(msg, \"s\", &service);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to parse register host message: %s\", strerror(-ret));\n\t\treturn ret;\n\t}\n\n\tstruct swaybar_watcher *watcher = data;\n\tif (list_seq_find(watcher->hosts, cmp_id, service) == -1) {\n\t\tsway_log(SWAY_DEBUG, \"Registering Status Notifier Host '%s'\", service);\n\t\tlist_add(watcher->hosts, strdup(service));\n\t\tif (watcher->hosts->length == 1) {\n\t\t\tsd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,\n\t\t\t\t\t\"IsStatusNotifierHostRegistered\", NULL);\n\t\t}\n\t\tsd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,\n\t\t\t\t\"StatusNotifierHostRegistered\", \"\");\n\t} else {\n\t\tsway_log(SWAY_DEBUG, \"Status Notifier Host '%s' already registered\", service);\n\t}\n\n\treturn sd_bus_reply_method_return(msg, \"\");\n}\n\nstatic int get_registered_snis(sd_bus *bus, const char *obj_path,\n\t\tconst char *interface, const char *property, sd_bus_message *reply,\n\t\tvoid *data, sd_bus_error *error) {\n\tstruct swaybar_watcher *watcher = data;\n\tlist_add(watcher->items, NULL); // strv expects NULL-terminated string array\n\tint ret = sd_bus_message_append_strv(reply, (char **)watcher->items->items);\n\tlist_del(watcher->items, watcher->items->length - 1);\n\treturn ret;\n}\n\nstatic int is_host_registered(sd_bus *bus, const char *obj_path,\n\t\tconst char *interface, const char *property, sd_bus_message *reply,\n\t\tvoid *data, sd_bus_error *error) {\n\tstruct swaybar_watcher *watcher = data;\n\tint val = watcher->hosts->length > 0; // dbus expects int rather than bool\n\treturn sd_bus_message_append_basic(reply, 'b', &val);\n}\n\nstatic const sd_bus_vtable watcher_vtable[] = {\n\tSD_BUS_VTABLE_START(0),\n\tSD_BUS_METHOD(\"RegisterStatusNotifierItem\", \"s\", \"\", register_sni,\n\t\t\tSD_BUS_VTABLE_UNPRIVILEGED),\n\tSD_BUS_METHOD(\"RegisterStatusNotifierHost\", \"s\", \"\", register_host,\n\t\t\tSD_BUS_VTABLE_UNPRIVILEGED),\n\tSD_BUS_PROPERTY(\"RegisteredStatusNotifierItems\", \"as\", get_registered_snis,\n\t\t\t0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),\n\tSD_BUS_PROPERTY(\"IsStatusNotifierHostRegistered\", \"b\", is_host_registered,\n\t\t\t0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),\n\tSD_BUS_PROPERTY(\"ProtocolVersion\", \"i\", NULL,\n\t\t\toffsetof(struct swaybar_watcher, version),\n\t\t\tSD_BUS_VTABLE_PROPERTY_CONST),\n\tSD_BUS_SIGNAL(\"StatusNotifierItemRegistered\", \"s\", 0),\n\tSD_BUS_SIGNAL(\"StatusNotifierItemUnregistered\", \"s\", 0),\n\tSD_BUS_SIGNAL(\"StatusNotifierHostRegistered\", NULL, 0),\n\tSD_BUS_VTABLE_END\n};\n\nstruct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) {\n\tstruct swaybar_watcher *watcher =\n\t\tcalloc(1, sizeof(struct swaybar_watcher));\n\tif (!watcher) {\n\t\treturn NULL;\n\t}\n\n\twatcher->interface = format_str(\"org.%s.StatusNotifierWatcher\", protocol);\n\n\tsd_bus_slot *signal_slot = NULL, *vtable_slot = NULL;\n\tint ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path,\n\t\t\twatcher->interface, watcher_vtable, watcher);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to add object vtable: %s\", strerror(-ret));\n\t\tgoto error;\n\t}\n\n\tret = sd_bus_match_signal(bus, &signal_slot, \"org.freedesktop.DBus\",\n\t\t\t\"/org/freedesktop/DBus\", \"org.freedesktop.DBus\",\n\t\t\t\"NameOwnerChanged\", handle_lost_service, watcher);\n\tif (ret < 0) {\n\t\tsway_log(SWAY_ERROR, \"Failed to subscribe to unregistering events: %s\",\n\t\t\t\tstrerror(-ret));\n\t\tgoto error;\n\t}\n\n\tret = sd_bus_request_name(bus, watcher->interface, 0);\n\tif (ret < 0) {\n\t\tif (-ret == EEXIST) {\n\t\t\tsway_log(SWAY_DEBUG, \"Failed to acquire service name '%s':\"\n\t\t\t\t\t\"another tray is already running\", watcher->interface);\n\t\t} else {\n\t\t\tsway_log(SWAY_ERROR, \"Failed to acquire service name '%s': %s\",\n\t\t\t\t\twatcher->interface, strerror(-ret));\n\t\t}\n\t\tgoto error;\n\t}\n\n\tsd_bus_slot_set_floating(signal_slot, 0);\n\tsd_bus_slot_set_floating(vtable_slot, 0);\n\n\twatcher->bus = bus;\n\twatcher->hosts = create_list();\n\twatcher->items = create_list();\n\twatcher->version = 0;\n\tsway_log(SWAY_DEBUG, \"Registered %s\", watcher->interface);\n\treturn watcher;\nerror:\n\tsd_bus_slot_unref(signal_slot);\n\tsd_bus_slot_unref(vtable_slot);\n\tdestroy_watcher(watcher);\n\treturn NULL;\n}\n\nvoid destroy_watcher(struct swaybar_watcher *watcher) {\n\tif (!watcher) {\n\t\treturn;\n\t}\n\tlist_free_items_and_destroy(watcher->hosts);\n\tlist_free_items_and_destroy(watcher->items);\n\tfree(watcher->interface);\n\tfree(watcher);\n}\n"
  },
  {
    "path": "swaymsg/main.c",
    "content": "\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <getopt.h>\n#include <stdint.h>\n#include <sys/un.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <ctype.h>\n#include <unistd.h>\n#include <json.h>\n#include \"stringop.h\"\n#include \"ipc-client.h\"\n#include \"log.h\"\n\nstatic bool success_object(json_object *result) {\n\tjson_object *success;\n\n\tif (!json_object_object_get_ex(result, \"success\", &success)) {\n\t\treturn true;\n\t}\n\n\treturn json_object_get_boolean(success);\n}\n\n// Iterate results array and return false if any of them failed\nstatic bool success(json_object *r, bool fallback) {\n\tif (!json_object_is_type(r, json_type_array)) {\n\t\tif (json_object_is_type(r, json_type_object)) {\n\t\t\treturn success_object(r);\n\t\t}\n\t\treturn fallback;\n\t}\n\n\tsize_t results_len = json_object_array_length(r);\n\tif (!results_len) {\n\t\treturn fallback;\n\t}\n\n\tfor (size_t i = 0; i < results_len; ++i) {\n\t\tjson_object *result = json_object_array_get_idx(r, i);\n\n\t\tif (!success_object(result)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic void pretty_print_cmd(json_object *r) {\n\tif (!success_object(r)) {\n\t\tjson_object *error;\n\t\tif (!json_object_object_get_ex(r, \"error\", &error)) {\n\t\t\tprintf(\"An unknown error occurred\");\n\t\t} else {\n\t\t\tprintf(\"Error: %s\\n\", json_object_get_string(error));\n\t\t}\n\t}\n}\n\nstatic void pretty_print_workspace(json_object *w) {\n\tjson_object *name, *rect, *visible, *output, *urgent, *layout,\n\t\t\t\t*representation, *focused;\n\tjson_object_object_get_ex(w, \"name\", &name);\n\tjson_object_object_get_ex(w, \"rect\", &rect);\n\tjson_object_object_get_ex(w, \"visible\", &visible);\n\tjson_object_object_get_ex(w, \"output\", &output);\n\tjson_object_object_get_ex(w, \"urgent\", &urgent);\n\tjson_object_object_get_ex(w, \"layout\", &layout);\n\tjson_object_object_get_ex(w, \"representation\", &representation);\n\tjson_object_object_get_ex(w, \"focused\", &focused);\n\tprintf(\n\t\t\"Workspace %s%s%s%s\\n\"\n\t\t\"  Output: %s\\n\"\n\t\t\"  Layout: %s\\n\"\n\t\t\"  Representation: %s\\n\\n\",\n\t\tjson_object_get_string(name),\n\t\tjson_object_get_boolean(focused) ? \" (focused)\" : \"\",\n\t\t!json_object_get_boolean(visible) ? \" (off-screen)\" : \"\",\n\t\tjson_object_get_boolean(urgent) ? \" (urgent)\" : \"\",\n\t\tjson_object_get_string(output),\n\t\tjson_object_get_string(layout),\n\t\tjson_object_get_string(representation)\n\t);\n}\n\nstatic const char *pretty_type_name(const char *name) {\n\t// TODO these constants probably belong in the common lib\n\tstruct {\n\t\tconst char *a;\n\t\tconst char *b;\n\t} type_names[] = {\n\t\t{ \"keyboard\", \"Keyboard\" },\n\t\t{ \"pointer\", \"Pointer\" },\n\t\t{ \"touchpad\", \"Touchpad\" },\n\t\t{ \"tablet_pad\", \"Tablet pad\" },\n\t\t{ \"tablet_tool\", \"Tablet tool\" },\n\t\t{ \"touch\", \"Touch\" },\n\t\t{ \"switch\", \"Switch\" },\n\t};\n\n\tfor (size_t i = 0; i < sizeof(type_names) / sizeof(type_names[0]); ++i) {\n\t\tif (strcmp(type_names[i].a, name) == 0) {\n\t\t\treturn type_names[i].b;\n\t\t}\n\t}\n\n\treturn name;\n}\n\nstatic void pretty_print_input(json_object *i) {\n\tjson_object *id, *name, *type, *product, *vendor, *kbdlayout, *libinput;\n\tjson_object_object_get_ex(i, \"identifier\", &id);\n\tjson_object_object_get_ex(i, \"name\", &name);\n\tjson_object_object_get_ex(i, \"type\", &type);\n\tjson_object_object_get_ex(i, \"product\", &product);\n\tjson_object_object_get_ex(i, \"vendor\", &vendor);\n\n\tconst char *fmt =\n\t\t\"Input device: %s\\n\"\n\t\t\"  Type: %s\\n\"\n\t\t\"  Identifier: %s\\n\"\n\t\t\"  Product ID: %d\\n\"\n\t\t\"  Vendor ID: %d\\n\";\n\n\n\tprintf(fmt, json_object_get_string(name),\n\t\tpretty_type_name(json_object_get_string(type)),\n\t\tjson_object_get_string(id),\n\t\tjson_object_get_int(product),\n\t\tjson_object_get_int(vendor));\n\n\tif (json_object_object_get_ex(i, \"xkb_active_layout_name\", &kbdlayout)) {\n\t\tconst char *layout = json_object_get_string(kbdlayout);\n\t\tprintf(\"  Active Keyboard Layout: %s\\n\", layout ? layout : \"(unnamed)\");\n\t}\n\n\tif (json_object_object_get_ex(i, \"libinput\", &libinput)) {\n\t\tjson_object *events;\n\t\tif (json_object_object_get_ex(libinput, \"send_events\", &events)) {\n\t\t\tprintf(\"  Libinput Send Events: %s\\n\",\n\t\t\t\t\tjson_object_get_string(events));\n\t\t}\n\t}\n\n\tprintf(\"\\n\");\n}\n\nstatic void pretty_print_seat(json_object *i) {\n\tjson_object *name, *capabilities, *devices;\n\tjson_object_object_get_ex(i, \"name\", &name);\n\tjson_object_object_get_ex(i, \"capabilities\", &capabilities);\n\tjson_object_object_get_ex(i, \"devices\", &devices);\n\n\tconst char *fmt =\n\t\t\"Seat: %s\\n\"\n\t\t\"  Capabilities: %d\\n\";\n\n\tprintf(fmt, json_object_get_string(name),\n\t\tjson_object_get_int(capabilities));\n\n\tsize_t devices_len = json_object_array_length(devices);\n\tif (devices_len > 0) {\n\t\tprintf(\"  Devices:\\n\");\n\t\tfor (size_t i = 0; i < devices_len; ++i) {\n\t\t\tjson_object *device = json_object_array_get_idx(devices, i);\n\n\t\t\tjson_object *device_name;\n\t\t\tjson_object_object_get_ex(device, \"name\", &device_name);\n\n\t\t\tprintf(\"    %s\\n\", json_object_get_string(device_name));\n\t\t}\n\t}\n\n\tprintf(\"\\n\");\n}\n\nstatic void pretty_print_output(json_object *o) {\n\tjson_object *name, *rect, *focused, *active, *power, *ws, *current_mode, *non_desktop;\n\tjson_object_object_get_ex(o, \"name\", &name);\n\tjson_object_object_get_ex(o, \"rect\", &rect);\n\tjson_object_object_get_ex(o, \"focused\", &focused);\n\tjson_object_object_get_ex(o, \"active\", &active);\n\tjson_object_object_get_ex(o, \"power\", &power);\n\tjson_object_object_get_ex(o, \"current_workspace\", &ws);\n\tjson_object_object_get_ex(o, \"non_desktop\", &non_desktop);\n\tjson_object *make, *model, *serial, *scale, *scale_filter, *subpixel,\n\t\t*transform, *max_render_time, *adaptive_sync_status, *allow_tearing,\n\t\t*hdr;\n\tjson_object_object_get_ex(o, \"make\", &make);\n\tjson_object_object_get_ex(o, \"model\", &model);\n\tjson_object_object_get_ex(o, \"serial\", &serial);\n\tjson_object_object_get_ex(o, \"scale\", &scale);\n\tjson_object_object_get_ex(o, \"scale_filter\", &scale_filter);\n\tjson_object_object_get_ex(o, \"subpixel_hinting\", &subpixel);\n\tjson_object_object_get_ex(o, \"transform\", &transform);\n\tjson_object_object_get_ex(o, \"max_render_time\", &max_render_time);\n\tjson_object_object_get_ex(o, \"adaptive_sync_status\", &adaptive_sync_status);\n\tjson_object_object_get_ex(o, \"allow_tearing\", &allow_tearing);\n\tjson_object_object_get_ex(o, \"hdr\", &hdr);\n\tjson_object *x, *y;\n\tjson_object_object_get_ex(rect, \"x\", &x);\n\tjson_object_object_get_ex(rect, \"y\", &y);\n\tjson_object *modes;\n\tjson_object_object_get_ex(o, \"modes\", &modes);\n\tjson_object *width, *height, *refresh;\n\tjson_object_object_get_ex(o, \"current_mode\", &current_mode);\n\tjson_object_object_get_ex(current_mode, \"width\", &width);\n\tjson_object_object_get_ex(current_mode, \"height\", &height);\n\tjson_object_object_get_ex(current_mode, \"refresh\", &refresh);\n\tjson_object *features, *features_adaptive_sync, *features_hdr;\n\tjson_object_object_get_ex(o, \"features\", &features);\n\tjson_object_object_get_ex(features, \"adaptive_sync\", &features_adaptive_sync);\n\tjson_object_object_get_ex(features, \"hdr\", &features_hdr);\n\n\tif (json_object_get_boolean(non_desktop)) {\n\t\tprintf(\n\t\t\t\"Output %s '%s %s %s' (non-desktop)\\n\",\n\t\t\tjson_object_get_string(name),\n\t\t\tjson_object_get_string(make),\n\t\t\tjson_object_get_string(model),\n\t\t\tjson_object_get_string(serial)\n\t\t);\n\t} else if (json_object_get_boolean(active)) {\n\t\tprintf(\n\t\t\t\"Output %s '%s %s %s'%s\\n\"\n\t\t\t\"  Current mode: %dx%d @ %.3f Hz\\n\"\n\t\t\t\"  Power: %s\\n\"\n\t\t\t\"  Position: %d,%d\\n\"\n\t\t\t\"  Scale factor: %f\\n\"\n\t\t\t\"  Scale filter: %s\\n\"\n\t\t\t\"  Subpixel hinting: %s\\n\"\n\t\t\t\"  Transform: %s\\n\"\n\t\t\t\"  Workspace: %s\\n\",\n\t\t\tjson_object_get_string(name),\n\t\t\tjson_object_get_string(make),\n\t\t\tjson_object_get_string(model),\n\t\t\tjson_object_get_string(serial),\n\t\t\tjson_object_get_boolean(focused) ? \" (focused)\" : \"\",\n\t\t\tjson_object_get_int(width),\n\t\t\tjson_object_get_int(height),\n\t\t\t(double)json_object_get_int(refresh) / 1000,\n\t\t\tjson_object_get_boolean(power) ? \"on\" : \"off\",\n\t\t\tjson_object_get_int(x), json_object_get_int(y),\n\t\t\tjson_object_get_double(scale),\n\t\t\tjson_object_get_string(scale_filter),\n\t\t\tjson_object_get_string(subpixel),\n\t\t\tjson_object_get_string(transform),\n\t\t\tjson_object_get_string(ws)\n\t\t);\n\n\t\tint max_render_time_int = json_object_get_int(max_render_time);\n\t\tprintf(\"  Max render time: \");\n\t\tprintf(max_render_time_int == 0 ? \"off\\n\" : \"%d ms\\n\", max_render_time_int);\n\n\t\tprintf(\"  Adaptive sync: %s\\n\",\n\t\t\tjson_object_get_boolean(features_adaptive_sync) ?\n\t\t\t\tjson_object_get_string(adaptive_sync_status) :\n\t\t\t\t\"unsupported\");\n\n\t\tprintf(\"  Allow tearing: %s\\n\",\n\t\t\tjson_object_get_boolean(allow_tearing) ? \"yes\" : \"no\");\n\n\t\tconst char *hdr_str = \"unsupported\";\n\t\tif (json_object_get_boolean(features_hdr)) {\n\t\t\thdr_str = json_object_get_boolean(hdr) ? \"on\" : \"off\";\n\t\t}\n\t\tprintf(\"  HDR: %s\\n\", hdr_str);\n\t} else {\n\t\tprintf(\n\t\t\t\"Output %s '%s %s %s' (disabled)\\n\",\n\t\t\tjson_object_get_string(name),\n\t\t\tjson_object_get_string(make),\n\t\t\tjson_object_get_string(model),\n\t\t\tjson_object_get_string(serial)\n\t\t);\n\t}\n\n\tsize_t modes_len = json_object_is_type(modes, json_type_array)\n\t\t? json_object_array_length(modes) : 0;\n\tif (modes_len > 0) {\n\t\tprintf(\"  Available modes:\\n\");\n\t\tfor (size_t i = 0; i < modes_len; ++i) {\n\t\t\tjson_object *mode = json_object_array_get_idx(modes, i);\n\n\t\t\tjson_object *mode_width, *mode_height, *mode_refresh,\n\t\t\t\t*mode_picture_aspect_ratio;\n\t\t\tjson_object_object_get_ex(mode, \"width\", &mode_width);\n\t\t\tjson_object_object_get_ex(mode, \"height\", &mode_height);\n\t\t\tjson_object_object_get_ex(mode, \"refresh\", &mode_refresh);\n\t\t\tjson_object_object_get_ex(mode, \"picture_aspect_ratio\",\n\t\t\t\t&mode_picture_aspect_ratio);\n\n\t\t\tprintf(\"    %dx%d @ %.3f Hz\", json_object_get_int(mode_width),\n\t\t\t\tjson_object_get_int(mode_height),\n\t\t\t\t(double)json_object_get_int(mode_refresh) / 1000);\n\t\t\tif (mode_picture_aspect_ratio &&\n\t\t\t\t\tstrcmp(\"none\", json_object_get_string(mode_picture_aspect_ratio)) != 0) {\n\t\t\t\tprintf(\" (%s)\", json_object_get_string(mode_picture_aspect_ratio));\n\t\t\t}\n\t\t\tprintf(\"\\n\");\n\t\t}\n\t}\n\n\tprintf(\"\\n\");\n}\n\nstatic void pretty_print_version(json_object *v) {\n\tjson_object *ver;\n\tjson_object_object_get_ex(v, \"human_readable\", &ver);\n\tprintf(\"sway version %s\\n\", json_object_get_string(ver));\n}\n\nstatic void pretty_print_config(json_object *c) {\n\tjson_object *config;\n\tjson_object_object_get_ex(c, \"config\", &config);\n\tprintf(\"%s\\n\", json_object_get_string(config));\n}\n\nstatic void pretty_print_tree(json_object *obj, int indent) {\n\tfor (int i = 0; i < indent; i++) {\n\t\tprintf(\"  \");\n\t}\n\n\tint id = json_object_get_int(json_object_object_get(obj, \"id\"));\n\tconst char *name = json_object_get_string(json_object_object_get(obj, \"name\"));\n\tconst char *type = json_object_get_string(json_object_object_get(obj, \"type\"));\n\tconst char *shell = json_object_get_string(json_object_object_get(obj, \"shell\"));\n\n\tprintf(\"#%d: %s \\\"%s\\\"\", id, type, name);\n\n\tif (shell != NULL) {\n\t\tint pid = json_object_get_int(json_object_object_get(obj, \"pid\"));\n\t\tconst char *app_id = json_object_get_string(json_object_object_get(obj, \"app_id\"));\n\t\tjson_object *window_props_obj = json_object_object_get(obj, \"window_properties\");\n\t\tconst char *instance = json_object_get_string(json_object_object_get(window_props_obj, \"instance\"));\n\t\tconst char *class = json_object_get_string(json_object_object_get(window_props_obj, \"class\"));\n\t\tint x11_id = json_object_get_int(json_object_object_get(obj, \"window\"));\n\t\tconst char *foreign_toplevel_id = json_object_get_string(json_object_object_get(obj, \"foreign_toplevel_identifier\"));\n\t\tconst char *sandbox_engine = json_object_get_string(json_object_object_get(obj, \"sandbox_engine\"));\n\t\tconst char *sandbox_app_id = json_object_get_string(json_object_object_get(obj, \"sandbox_app_id\"));\n\t\tconst char *sandbox_instance_id = json_object_get_string(json_object_object_get(obj, \"sandbox_instance_id\"));\n\n\t\tprintf(\" (%s, pid: %d\", shell, pid);\n\t\tif (app_id != NULL) {\n\t\t\tprintf(\", app_id: \\\"%s\\\"\", app_id);\n\t\t}\n\t\tif (instance != NULL) {\n\t\t\tprintf(\", instance: \\\"%s\\\"\", instance);\n\t\t}\n\t\tif (class != NULL) {\n\t\t\tprintf(\", class: \\\"%s\\\"\", class);\n\t\t}\n\t\tif (x11_id != 0) {\n\t\t\tprintf(\", X11 window: 0x%X\", x11_id);\n\t\t}\n\t\tif (foreign_toplevel_id != NULL) {\n\t\t\tprintf(\", foreign_toplevel_id: \\\"%s\\\"\", foreign_toplevel_id);\n\t\t}\n\t\tif (sandbox_engine != NULL) {\n\t\t\tprintf(\", sandbox_engine: \\\"%s\\\"\", sandbox_engine);\n\t\t}\n\t\tif (sandbox_app_id != NULL) {\n\t\t\tprintf(\", sandbox_app_id: \\\"%s\\\"\", sandbox_app_id);\n\t\t}\n\t\tif (sandbox_instance_id != NULL) {\n\t\t\tprintf(\", sandbox_instance_id: \\\"%s\\\"\", sandbox_instance_id);\n\t\t}\n\t\tprintf(\")\");\n\t}\n\n\tprintf(\"\\n\");\n\n\tjson_object *nodes_obj = json_object_object_get(obj, \"nodes\");\n\tsize_t len = json_object_array_length(nodes_obj);\n\tfor (size_t i = 0; i < len; i++) {\n\t\tpretty_print_tree(json_object_array_get_idx(nodes_obj, i), indent + 1);\n\t}\n\n\tjson_object *floating_nodes_obj;\n\tjson_bool floating_nodes = json_object_object_get_ex(obj, \"floating_nodes\", &floating_nodes_obj);\n\tif (floating_nodes) {\n\t\tsize_t len = json_object_array_length(floating_nodes_obj);\n\t\tfor (size_t i = 0; i < len; i++) {\n\t\t\tpretty_print_tree(json_object_array_get_idx(floating_nodes_obj, i), indent + 1);\n\t\t}\n\t}\n}\n\nstatic void pretty_print(int type, json_object *resp) {\n\tswitch (type) {\n\tcase IPC_SEND_TICK:\n\t\treturn;\n\tcase IPC_GET_VERSION:\n\t\tpretty_print_version(resp);\n\t\treturn;\n\tcase IPC_GET_CONFIG:\n\t\tpretty_print_config(resp);\n\t\treturn;\n\tcase IPC_GET_TREE:\n\t\tpretty_print_tree(resp, 0);\n\t\treturn;\n\tcase IPC_COMMAND:\n\tcase IPC_GET_WORKSPACES:\n\tcase IPC_GET_INPUTS:\n\tcase IPC_GET_OUTPUTS:\n\tcase IPC_GET_SEATS:\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"%s\\n\", json_object_to_json_string_ext(resp,\n\t\t\tJSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));\n\t\treturn;\n\t}\n\n\tjson_object *obj;\n\tsize_t len = json_object_array_length(resp);\n\tfor (size_t i = 0; i < len; ++i) {\n\t\tobj = json_object_array_get_idx(resp, i);\n\t\tswitch (type) {\n\t\tcase IPC_COMMAND:\n\t\t\tpretty_print_cmd(obj);\n\t\t\tbreak;\n\t\tcase IPC_GET_WORKSPACES:\n\t\t\tpretty_print_workspace(obj);\n\t\t\tbreak;\n\t\tcase IPC_GET_INPUTS:\n\t\t\tpretty_print_input(obj);\n\t\t\tbreak;\n\t\tcase IPC_GET_OUTPUTS:\n\t\t\tpretty_print_output(obj);\n\t\t\tbreak;\n\t\tcase IPC_GET_SEATS:\n\t\t\tpretty_print_seat(obj);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nint main(int argc, char **argv) {\n\tstatic bool quiet = false;\n\tstatic bool raw = false;\n\tstatic bool monitor = false;\n\tchar *socket_path = NULL;\n\tchar *cmdtype = NULL;\n\n\tsway_log_init(SWAY_INFO, NULL);\n\n\tstatic const struct option long_options[] = {\n\t\t{\"help\", no_argument, NULL, 'h'},\n\t\t{\"monitor\", no_argument, NULL, 'm'},\n\t\t{\"pretty\", no_argument, NULL, 'p'},\n\t\t{\"quiet\", no_argument, NULL, 'q'},\n\t\t{\"raw\", no_argument, NULL, 'r'},\n\t\t{\"socket\", required_argument, NULL, 's'},\n\t\t{\"type\", required_argument, NULL, 't'},\n\t\t{\"version\", no_argument, NULL, 'v'},\n\t\t{0, 0, 0, 0}\n\t};\n\n\tconst char *usage =\n\t\t\"Usage: swaymsg [options] [message]\\n\"\n\t\t\"\\n\"\n\t\t\"  -h, --help             Show help message and quit.\\n\"\n\t\t\"  -m, --monitor          Monitor until killed (-t SUBSCRIBE only)\\n\"\n\t\t\"  -p, --pretty           Use pretty output even when not using a tty\\n\"\n\t\t\"  -q, --quiet            Be quiet.\\n\"\n\t\t\"  -r, --raw              Use raw output even if using a tty\\n\"\n\t\t\"  -s, --socket <socket>  Use the specified socket.\\n\"\n\t\t\"  -t, --type <type>      Specify the message type.\\n\"\n\t\t\"  -v, --version          Show the version number and quit.\\n\";\n\n\traw = !isatty(STDOUT_FILENO);\n\n\tint c;\n\twhile (1) {\n\t\tint option_index = 0;\n\t\tc = getopt_long(argc, argv, \"hmpqrs:t:v\", long_options, &option_index);\n\t\tif (c == -1) {\n\t\t\tbreak;\n\t\t}\n\t\tswitch (c) {\n\t\tcase 'm': // Monitor\n\t\t\tmonitor = true;\n\t\t\tbreak;\n\t\tcase 'p': // Pretty\n\t\t\traw = false;\n\t\t\tbreak;\n\t\tcase 'q': // Quiet\n\t\t\tquiet = true;\n\t\t\tbreak;\n\t\tcase 'r': // Raw\n\t\t\traw = true;\n\t\t\tbreak;\n\t\tcase 's': // Socket\n\t\t\tsocket_path = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 't': // Type\n\t\t\tcmdtype = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tprintf(\"swaymsg version \" SWAY_VERSION \"\\n\");\n\t\t\texit(EXIT_SUCCESS);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfprintf(stderr, \"%s\", usage);\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t}\n\n\tif (!cmdtype) {\n\t\tcmdtype = strdup(\"command\");\n\t}\n\tif (!socket_path) {\n\t\tsocket_path = get_socketpath();\n\t\tif (!socket_path) {\n\t\t\tif (quiet) {\n\t\t\t\texit(EXIT_FAILURE);\n\t\t\t}\n\t\t\tsway_abort(\"Unable to retrieve socket path\");\n\t\t}\n\t}\n\n\tuint32_t type = IPC_COMMAND;\n\n\tif (strcasecmp(cmdtype, \"command\") == 0) {\n\t\ttype = IPC_COMMAND;\n\t} else if (strcasecmp(cmdtype, \"get_workspaces\") == 0) {\n\t\ttype = IPC_GET_WORKSPACES;\n\t} else if (strcasecmp(cmdtype, \"get_seats\") == 0) {\n\t\ttype = IPC_GET_SEATS;\n\t} else if (strcasecmp(cmdtype, \"get_inputs\") == 0) {\n\t\ttype = IPC_GET_INPUTS;\n\t} else if (strcasecmp(cmdtype, \"get_outputs\") == 0) {\n\t\ttype = IPC_GET_OUTPUTS;\n\t} else if (strcasecmp(cmdtype, \"get_tree\") == 0) {\n\t\ttype = IPC_GET_TREE;\n\t} else if (strcasecmp(cmdtype, \"get_marks\") == 0) {\n\t\ttype = IPC_GET_MARKS;\n\t} else if (strcasecmp(cmdtype, \"get_bar_config\") == 0) {\n\t\ttype = IPC_GET_BAR_CONFIG;\n\t} else if (strcasecmp(cmdtype, \"get_version\") == 0) {\n\t\ttype = IPC_GET_VERSION;\n\t} else if (strcasecmp(cmdtype, \"get_binding_modes\") == 0) {\n\t\ttype = IPC_GET_BINDING_MODES;\n\t} else if (strcasecmp(cmdtype, \"get_binding_state\") == 0) {\n\t\ttype = IPC_GET_BINDING_STATE;\n\t} else if (strcasecmp(cmdtype, \"get_config\") == 0) {\n\t\ttype = IPC_GET_CONFIG;\n\t} else if (strcasecmp(cmdtype, \"send_tick\") == 0) {\n\t\ttype = IPC_SEND_TICK;\n\t} else if (strcasecmp(cmdtype, \"subscribe\") == 0) {\n\t\ttype = IPC_SUBSCRIBE;\n\t} else {\n\t\tif (quiet) {\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t\tsway_abort(\"Unknown message type %s\", cmdtype);\n\t}\n\n\tfree(cmdtype);\n\n\tif (monitor && type != IPC_SUBSCRIBE) {\n\t\tif (!quiet) {\n\t\t\tsway_log(SWAY_ERROR, \"Monitor can only be used with -t SUBSCRIBE\");\n\t\t}\n\t\tfree(socket_path);\n\t\treturn 1;\n\t}\n\n\tchar *command = NULL;\n\tif (optind < argc) {\n\t\tcommand = join_args(argv + optind, argc - optind);\n\t} else {\n\t\tcommand = strdup(\"\");\n\t}\n\n\tint ret = 0;\n\tint socketfd = ipc_open_socket(socket_path);\n\tstruct timeval timeout = {.tv_sec = 3, .tv_usec = 0};\n\tipc_set_recv_timeout(socketfd, timeout);\n\tuint32_t len = strlen(command);\n\tchar *resp = ipc_single_command(socketfd, type, command, &len);\n\n\t// pretty print the json\n\tjson_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);\n\tif (tok == NULL) {\n\t\tif (quiet) {\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t\tsway_abort(\"failed allocating json_tokener\");\n\t}\n\tjson_object *obj = json_tokener_parse_ex(tok, resp, -1);\n\tenum json_tokener_error err = json_tokener_get_error(tok);\n\tjson_tokener_free(tok);\n\tif (obj == NULL || err != json_tokener_success) {\n\t\tif (!quiet) {\n\t\t\tsway_log(SWAY_ERROR, \"failed to parse payload as json: %s\",\n\t\t\t\tjson_tokener_error_desc(err));\n\t\t}\n\t\tret = 1;\n\t} else {\n\t\tif (!success(obj, true)) {\n\t\t\tret = 2;\n\t\t}\n\t\tif (!quiet && (type != IPC_SUBSCRIBE  || ret != 0)) {\n\t\t\tif (raw) {\n\t\t\t\tprintf(\"%s\\n\", json_object_to_json_string_ext(obj,\n\t\t\t\t\tJSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));\n\t\t\t} else {\n\t\t\t\tpretty_print(type, obj);\n\t\t\t}\n\t\t}\n\t\tjson_object_put(obj);\n\t}\n\tfree(command);\n\tfree(resp);\n\n\tif (type == IPC_SUBSCRIBE && ret == 0) {\n\t\t// Remove the timeout for subscribed events\n\t\ttimeout.tv_sec = 0;\n\t\ttimeout.tv_usec = 0;\n\t\tipc_set_recv_timeout(socketfd, timeout);\n\n\t\tdo {\n\t\t\tstruct ipc_response *reply = ipc_recv_response(socketfd);\n\t\t\tif (!reply) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tjson_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);\n\t\t\tif (tok == NULL) {\n\t\t\t\tif (quiet) {\n\t\t\t\t\texit(EXIT_FAILURE);\n\t\t\t\t}\n\t\t\t\tsway_abort(\"failed allocating json_tokener\");\n\t\t\t}\n\t\t\tjson_object *obj = json_tokener_parse_ex(tok, reply->payload, -1);\n\t\t\tenum json_tokener_error err = json_tokener_get_error(tok);\n\t\t\tjson_tokener_free(tok);\n\t\t\tif (obj == NULL || err != json_tokener_success) {\n\t\t\t\tif (!quiet) {\n\t\t\t\t\tsway_log(SWAY_ERROR, \"failed to parse payload as json: %s\",\n\t\t\t\t\t\tjson_tokener_error_desc(err));\n\t\t\t\t}\n\t\t\t\tret = 1;\n\t\t\t\tbreak;\n\t\t\t} else if (quiet) {\n\t\t\t\tjson_object_put(obj);\n\t\t\t} else {\n\t\t\t\tif (raw) {\n\t\t\t\t\tprintf(\"%s\\n\", json_object_to_json_string(obj));\n\t\t\t\t} else {\n\t\t\t\t\tprintf(\"%s\\n\", json_object_to_json_string_ext(obj,\n\t\t\t\t\t\tJSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));\n\t\t\t\t}\n\t\t\t\tfflush(stdout);\n\t\t\t\tjson_object_put(obj);\n\t\t\t}\n\n\t\t\tfree_ipc_response(reply);\n\t\t} while (monitor);\n\t}\n\n\tclose(socketfd);\n\tfree(socket_path);\n\treturn ret;\n}\n"
  },
  {
    "path": "swaymsg/meson.build",
    "content": "executable(\n\t'swaymsg',\n\t'main.c',\n\tinclude_directories: [sway_inc],\n\tdependencies: [jsonc],\n\tlink_with: [lib_sway_common],\n\tinstall: true\n)\n"
  },
  {
    "path": "swaymsg/swaymsg.1.scd",
    "content": "swaymsg(1)\n\n# NAME\n\nswaymsg - Send messages to a running instance of sway over the IPC socket.\n\n# SYNOPSIS\n\n_swaymsg_ [options...] [message]\n\n# OPTIONS\n\n*-h, --help*\n\tShow help message and quit.\n\n*-m, --monitor*\n\tMonitor for responses until killed instead of exiting after the first\n\tresponse. This can only be used with the IPC message type _subscribe_. If\n\tthere is a malformed response or an invalid event type was requested,\n\tswaymsg will stop monitoring and exit.\n\n*-p, --pretty*\n\tUse pretty output even when not using a tty.\n\tNot available for all message types.\n\n*-q, --quiet*\n\tSends the IPC message but does not print the response from sway.\n\n*-r, --raw*\n\tUse raw JSON output even if using a tty.\n\n*-s, --socket* <path>\n\tUse the specified socket path. Otherwise, swaymsg will ask sway where the\n\tsocket is (which is the value of $SWAYSOCK, then of $I3SOCK).\n\n*-t, --type* <type>\n\tSpecify the type of IPC message. See below.\n\n*-v, --version*\n\tPrint the version (of swaymsg) and quit.\n\n# IPC MESSAGE TYPES\n\n*<command>*\n\tThe message is a sway command (the same commands you can bind to keybindings\n\tin your sway config file). It will be executed immediately.\n\n\tSee *sway*(5) for a list of commands.\n\n\t_swaymsg_ can return pretty printed (standalone-default) or JSON-formatted\n\t(*--raw*) output. For detailed documentation on the returned JSON-data of\n\teach message type listed below,\trefer to *sway-ipc*(7). The JSON-format can\n\tcontain more information than the pretty print.\n\n\tTips:\n\t- Command expansion is performed twice: once by swaymsg, and again by sway.\n\t  If you have quoted multi-word strings in your command, enclose the entire\n\t  command in single-quotes. For example, use\n\t  _swaymsg 'output \"Foobar Display\" enable'_ instead of\n\t  _swaymsg output \"Foobar Display\" enable_. Furthermore, note that comma\n\t  separated options also count as multi-word strings, because commas can be\n\t  used to execute commands on the same line.\n\t- If you are providing a command that contains a leading hyphen (_-_), insert\n\t  two hyphens (_--_) before the command to signal to swaymsg not to parse\n\t  anything beyond that point as an option. For example, use\n\t  _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_.\n\n*get\\_workspaces*\n\tGets a list of workspaces and their status.\n\n*get\\_inputs*\n\tGets a list of current inputs.\n\n*get\\_outputs*\n\tGets a list of current outputs.\n\n*get\\_tree*\n\tGets a JSON-encoded layout tree of all open windows, containers, outputs,\n\tworkspaces, and so on.\n\n*get\\_seats*\n\tGets a list of all seats,\n\tits properties and all assigned devices.\n\n*get\\_marks*\n\tGet a JSON-encoded list of marks.\n\n*get\\_bar\\_config*\n\tGet a JSON-encoded configuration for swaybar.\n\n*get\\_version*\n\tGet version information for the running instance of sway.\n\n*get\\_binding\\_modes*\n\tGets a JSON-encoded list of currently configured binding modes.\n\n*get\\_binding\\_state*\n\tGets JSON-encoded info about the current binding state.\n\n*get\\_config*\n\tGets a copy of the current configuration. Doesn't expand includes.\n\n*send\\_tick*\n\tSends a tick event to all subscribed clients.\n\n*subscribe*\n\tSubscribe to a list of event types. The argument for this type should be\n\tprovided in the form of a valid JSON array. If any of the types are invalid\n\tor if a valid JSON array is not provided, this will result in a failure.\n\tFor a list of valid event types and the data returned with them refer to\n\t*sway-ipc*(7).\n\n# RETURN CODES\n\n*0*\n\tSuccess\n\n*1*\n\tswaymsg errors such as invalid syntax, unable to connect to the ipc socket\n\tor unable to parse sway's reply\n\n*2*\n\tSway returned an error when processing the command (ex. invalid command,\n\tcommand failed, and invalid subscription request)\n\n# SEE ALSO\n\n*sway*(5) *sway-bar*(5) *sway-input*(5) *sway-output*(5) *sway-ipc*(7)\n"
  },
  {
    "path": "swaynag/config.c",
    "content": "#include <getopt.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <wordexp.h>\n#include <unistd.h>\n#include \"log.h\"\n#include \"list.h\"\n#include \"swaynag/swaynag.h\"\n#include \"swaynag/types.h\"\n#include \"util.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n\nstatic char *read_and_trim_stdin(void) {\n\tchar *buffer = NULL, *line = NULL;\n\tsize_t buffer_len = 0, line_size = 0;\n\twhile (1) {\n\t\tssize_t nread = getline(&line, &line_size, stdin);\n\t\tif (nread == -1) {\n\t\t\tif (feof(stdin)) {\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tperror(\"getline\");\n\t\t\t\tgoto freeline;\n\t\t\t}\n\t\t}\n\t\tbuffer = realloc(buffer, buffer_len + nread + 1);\n\t\tif (!buffer) {\n\t\t\tperror(\"realloc\");\n\t\t\tgoto freebuf;\n\t\t}\n\t\tmemcpy(&buffer[buffer_len], line, nread + 1);\n\t\tbuffer_len += nread;\n\t}\n\tfree(line);\n\n\twhile (buffer_len && buffer[buffer_len - 1] == '\\n') {\n\t\tbuffer[--buffer_len] = '\\0';\n\t}\n\n\treturn buffer;\n\nfreeline:\n\tfree(line);\nfreebuf:\n\tfree(buffer);\n\treturn NULL;\n}\n\nint swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,\n\t\tlist_t *types, struct swaynag_type *type, char **config, bool *debug) {\n\tenum type_options {\n\t\tTO_COLOR_BACKGROUND = 256,\n\t\tTO_COLOR_BORDER,\n\t\tTO_COLOR_BORDER_BOTTOM,\n\t\tTO_COLOR_BUTTON,\n\t\tTO_COLOR_DETAILS,\n\t\tTO_COLOR_TEXT,\n\t\tTO_COLOR_BUTTON_TEXT,\n\t\tTO_THICK_BAR_BORDER,\n\t\tTO_PADDING_MESSAGE,\n\t\tTO_THICK_DET_BORDER,\n\t\tTO_THICK_BTN_BORDER,\n\t\tTO_GAP_BTN,\n\t\tTO_GAP_BTN_DISMISS,\n\t\tTO_MARGIN_BTN_RIGHT,\n\t\tTO_PADDING_BTN,\n\t};\n\n\tstatic const struct option opts[] = {\n\t\t{\"button\", required_argument, NULL, 'b'},\n\t\t{\"button-no-terminal\", required_argument, NULL, 'B'},\n\t\t{\"button-dismiss\", required_argument, NULL, 'z'},\n\t\t{\"button-dismiss-no-terminal\", required_argument, NULL, 'Z'},\n\t\t{\"config\", required_argument, NULL, 'c'},\n\t\t{\"debug\", no_argument, NULL, 'd'},\n\t\t{\"edge\", required_argument, NULL, 'e'},\n\t\t{\"layer\", required_argument, NULL, 'y'},\n\t\t{\"font\", required_argument, NULL, 'f'},\n\t\t{\"help\", no_argument, NULL, 'h'},\n\t\t{\"detailed-message\", no_argument, NULL, 'l'},\n\t\t{\"detailed-button\", required_argument, NULL, 'L'},\n\t\t{\"message\", required_argument, NULL, 'm'},\n\t\t{\"output\", required_argument, NULL, 'o'},\n\t\t{\"dismiss-button\", required_argument, NULL, 's'},\n\t\t{\"type\", required_argument, NULL, 't'},\n\t\t{\"version\", no_argument, NULL, 'v'},\n\n\t\t{\"background\", required_argument, NULL, TO_COLOR_BACKGROUND},\n\t\t{\"border\", required_argument, NULL, TO_COLOR_BORDER},\n\t\t{\"border-bottom\", required_argument, NULL, TO_COLOR_BORDER_BOTTOM},\n\t\t{\"button-background\", required_argument, NULL, TO_COLOR_BUTTON},\n\t\t{\"text\", required_argument, NULL, TO_COLOR_TEXT},\n\t\t{\"button-text\", required_argument, NULL, TO_COLOR_BUTTON_TEXT},\n\t\t{\"border-bottom-size\", required_argument, NULL, TO_THICK_BAR_BORDER},\n\t\t{\"message-padding\", required_argument, NULL, TO_PADDING_MESSAGE},\n\t\t{\"details-border-size\", required_argument, NULL, TO_THICK_DET_BORDER},\n\t\t{\"details-background\", required_argument, NULL, TO_COLOR_DETAILS},\n\t\t{\"button-border-size\", required_argument, NULL, TO_THICK_BTN_BORDER},\n\t\t{\"button-gap\", required_argument, NULL, TO_GAP_BTN},\n\t\t{\"button-dismiss-gap\", required_argument, NULL, TO_GAP_BTN_DISMISS},\n\t\t{\"button-margin-right\", required_argument, NULL, TO_MARGIN_BTN_RIGHT},\n\t\t{\"button-padding\", required_argument, NULL, TO_PADDING_BTN},\n\n\t\t{0, 0, 0, 0}\n\t};\n\n\tconst char *usage =\n\t\t\"Usage: swaynag [options...]\\n\"\n\t\t\"\\n\"\n\t\t\"  -b, --button <text> <action>  Create a button with text that \"\n\t\t\t\"executes action in a terminal when pressed. Multiple buttons can \"\n\t\t\t\"be defined.\\n\"\n\t\t\"  -B, --button-no-terminal <text> <action>  Like --button, but does\"\n\t\t\t\"not run the action in a terminal.\\n\"\n\t\t\"  -z, --button-dismiss <text> <action>  Create a button with text that \"\n\t\t\t\"dismisses swaynag, and executes action in a terminal when pressed. \"\n\t\t\t\"Multiple buttons can be defined.\\n\"\n\t\t\"  -Z, --button-dismiss-no-terminal <text> <action>  Like \"\n\t\t\t\"--button-dismiss, but does not run the action in a terminal.\\n\"\n\t\t\"  -c, --config <path>             Path to config file.\\n\"\n\t\t\"  -d, --debug                     Enable debugging.\\n\"\n\t\t\"  -e, --edge top|bottom           Set the edge to use.\\n\"\n\t\t\"  -y, --layer overlay|top|bottom|background\\n\"\n\t    \"                                  Set the layer to use.\\n\"\n\t\t\"  -f, --font <font>               Set the font to use.\\n\"\n\t\t\"  -h, --help                      Show help message and quit.\\n\"\n\t\t\"  -l, --detailed-message          Read a detailed message from stdin.\\n\"\n\t\t\"  -L, --detailed-button <text>    Set the text of the detail button.\\n\"\n\t\t\"  -m, --message <msg>             Set the message text.\\n\"\n\t\t\"  -o, --output <output>           Set the output to use.\\n\"\n\t\t\"  -s, --dismiss-button <text>     Set the dismiss button text.\\n\"\n\t\t\"  -t, --type <type>               Set the message type.\\n\"\n\t\t\"  -v, --version                   Show the version number and quit.\\n\"\n\t\t\"\\n\"\n\t\t\"The following appearance options can also be given:\\n\"\n\t\t\"  --background RRGGBB[AA]         Background color.\\n\"\n\t\t\"  --border RRGGBB[AA]             Border color.\\n\"\n\t\t\"  --border-bottom RRGGBB[AA]      Bottom border color.\\n\"\n\t\t\"  --button-background RRGGBB[AA]  Button background color.\\n\"\n\t\t\"  --text RRGGBB[AA]               Text color.\\n\"\n\t\t\"  --button-text RRGGBB[AA]        Button text color.\\n\"\n\t\t\"  --border-bottom-size size       Thickness of the bar border.\\n\"\n\t\t\"  --message-padding padding       Padding for the message.\\n\"\n\t\t\"  --details-border-size size      Thickness for the details border.\\n\"\n\t\t\"  --details-background RRGGBB[AA] Details background color.\\n\"\n\t\t\"  --button-border-size size       Thickness for the button border.\\n\"\n\t\t\"  --button-gap gap                Size of the gap between buttons\\n\"\n\t\t\"  --button-dismiss-gap gap        Size of the gap for dismiss button.\\n\"\n\t\t\"  --button-margin-right margin    Margin from dismiss button to edge.\\n\"\n\t\t\"  --button-padding padding        Padding for the button text.\\n\";\n\n\toptind = 1;\n\twhile (1) {\n\t\tint c = getopt_long(argc, argv, \"b:B:z:Z:c:de:y:f:hlL:m:o:s:t:v\", opts, NULL);\n\t\tif (c == -1) {\n\t\t\tbreak;\n\t\t}\n\t\tswitch (c) {\n\t\tcase 'b': // Button\n\t\tcase 'B': // Button (No Terminal)\n\t\tcase 'z': // Button (Dismiss)\n\t\tcase 'Z': // Button (Dismiss, No Terminal)\n\t\t\tif (swaynag) {\n\t\t\t\tif (optind >= argc) {\n\t\t\t\t\tfprintf(stderr, \"Missing action for button %s\\n\", optarg);\n\t\t\t\t\treturn EXIT_FAILURE;\n\t\t\t\t}\n\t\t\t\tstruct swaynag_button *button = calloc(1, sizeof(struct swaynag_button));\n\t\t\t\tif (!button) {\n\t\t\t\t\tperror(\"calloc\");\n\t\t\t\t\treturn EXIT_FAILURE;\n\t\t\t\t}\n\t\t\t\tbutton->text = strdup(optarg);\n\t\t\t\tbutton->type = SWAYNAG_ACTION_COMMAND;\n\t\t\t\tbutton->action = strdup(argv[optind]);\n\t\t\t\tbutton->terminal = c == 'b';\n\t\t\t\tbutton->dismiss = c == 'z' || c == 'Z';\n\t\t\t\tlist_add(swaynag->buttons, button);\n\t\t\t}\n\t\t\toptind++;\n\t\t\tbreak;\n\t\tcase 'c': // Config\n\t\t\tif (config) {\n\t\t\t\t*config = strdup(optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'd': // Debug\n\t\t\tif (debug) {\n\t\t\t\t*debug = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'e': // Edge\n\t\t\tif (type) {\n\t\t\t\tif (strcmp(optarg, \"top\") == 0) {\n\t\t\t\t\ttype->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP\n\t\t\t\t\t\t| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT\n\t\t\t\t\t\t| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;\n\t\t\t\t} else if (strcmp(optarg, \"bottom\") == 0) {\n\t\t\t\t\ttype->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM\n\t\t\t\t\t\t| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT\n\t\t\t\t\t\t| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;\n\t\t\t\t} else {\n\t\t\t\t\tfprintf(stderr, \"Invalid edge: %s\\n\", optarg);\n\t\t\t\t\treturn EXIT_FAILURE;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'y': // Layer\n\t\t\tif (type) {\n\t\t\t\tif (strcmp(optarg, \"background\") == 0) {\n\t\t\t\t\ttype->layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;\n\t\t\t\t} else if (strcmp(optarg, \"bottom\") == 0) {\n\t\t\t\t\ttype->layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;\n\t\t\t\t} else if (strcmp(optarg, \"top\") == 0) {\n\t\t\t\t\ttype->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;\n\t\t\t\t} else if (strcmp(optarg, \"overlay\") == 0) {\n\t\t\t\t\ttype->layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;\n\t\t\t\t} else {\n\t\t\t\t\tfprintf(stderr, \"Invalid layer: %s\\n\"\n\t\t\t\t\t\t\t\"Usage: --layer overlay|top|bottom|background\\n\",\n\t\t\t\t\t\t\toptarg);\n\t\t\t\t\treturn EXIT_FAILURE;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'f': // Font\n\t\t\tif (type) {\n\t\t\t\tpango_font_description_free(type->font_description);\n\t\t\t\ttype->font_description = pango_font_description_from_string(optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'l': // Detailed Message\n\t\t\tif (swaynag) {\n\t\t\t\tfree(swaynag->details.message);\n\t\t\t\tswaynag->details.message = read_and_trim_stdin();\n\t\t\t\tif (!swaynag->details.message) {\n\t\t\t\t\treturn EXIT_FAILURE;\n\t\t\t\t}\n\t\t\t\tswaynag->details.button_up.text = strdup(\"▲\");\n\t\t\t\tswaynag->details.button_down.text = strdup(\"▼\");\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'L': // Detailed Button Text\n\t\t\tif (swaynag) {\n\t\t\t\tfree(swaynag->details.details_text);\n\t\t\t\tswaynag->details.details_text = strdup(optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'm': // Message\n\t\t\tif (swaynag) {\n\t\t\t\tfree(swaynag->message);\n\t\t\t\tswaynag->message = strdup(optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'o': // Output\n\t\t\tif (type) {\n\t\t\t\tfree(type->output);\n\t\t\t\ttype->output = strdup(optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 's': // Dismiss Button Text\n\t\t\tif (swaynag) {\n\t\t\t\tstruct swaynag_button *button_close = swaynag->buttons->items[0];\n\t\t\t\tfree(button_close->text);\n\t\t\t\tbutton_close->text = strdup(optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 't': // Type\n\t\t\tif (swaynag) {\n\t\t\t\tswaynag->type = swaynag_type_get(types, optarg);\n\t\t\t\tif (!swaynag->type) {\n\t\t\t\t\tfprintf(stderr, \"Unknown type %s\\n\", optarg);\n\t\t\t\t\treturn EXIT_FAILURE;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'v': // Version\n\t\t\tprintf(\"swaynag version \" SWAY_VERSION \"\\n\");\n\t\t\treturn -1;\n\t\tcase TO_COLOR_BACKGROUND: // Background color\n\t\t\tif (type && !parse_color(optarg, &type->background)) {\n\t\t\t\tfprintf(stderr, \"Invalid background color: %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_COLOR_BORDER: // Border color\n\t\t\tif (type && !parse_color(optarg, &type->border)) {\n\t\t\t\tfprintf(stderr, \"Invalid border color: %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_COLOR_BORDER_BOTTOM: // Bottom border color\n\t\t\tif (type && !parse_color(optarg, &type->border_bottom)) {\n\t\t\t\tfprintf(stderr, \"Invalid border bottom color: %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_COLOR_BUTTON:  // Button background color\n\t\t\tif (type && !parse_color(optarg, &type->button_background)) {\n\t\t\t\tfprintf(stderr, \"Invalid button background color: %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_COLOR_DETAILS:  // Details background color\n\t\t\tif (type && !parse_color(optarg, &type->details_background)) {\n\t\t\t\tfprintf(stderr, \"Invalid details background color: %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_COLOR_TEXT:  // Text color\n\t\t\tif (type && !parse_color(optarg, &type->text)) {\n\t\t\t\tfprintf(stderr, \"Invalid text color: %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_COLOR_BUTTON_TEXT:  // Button text color\n\t\t\tif (type && !parse_color(optarg, &type->button_text)) {\n\t\t\t\tfprintf(stderr, \"Invalid button text color: %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_THICK_BAR_BORDER:  // Bottom border thickness\n\t\t\tif (type) {\n\t\t\t\ttype->bar_border_thickness = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_PADDING_MESSAGE:  // Message padding\n\t\t\tif (type) {\n\t\t\t\ttype->message_padding = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_THICK_DET_BORDER:  // Details border thickness\n\t\t\tif (type) {\n\t\t\t\ttype->details_border_thickness = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_THICK_BTN_BORDER:  // Button border thickness\n\t\t\tif (type) {\n\t\t\t\ttype->button_border_thickness = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_GAP_BTN: // Gap between buttons\n\t\t\tif (type) {\n\t\t\t\ttype->button_gap = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_GAP_BTN_DISMISS:  // Gap between dismiss button\n\t\t\tif (type) {\n\t\t\t\ttype->button_gap_close = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_MARGIN_BTN_RIGHT:  // Margin on the right side of button area\n\t\t\tif (type) {\n\t\t\t\ttype->button_margin_right = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TO_PADDING_BTN:  // Padding for the button text\n\t\t\tif (type) {\n\t\t\t\ttype->button_padding = strtol(optarg, NULL, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault: // Help or unknown flag\n\t\t\tfprintf(c == 'h' ? stdout : stderr, \"%s\", usage);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstatic bool file_exists(const char *path) {\n\treturn path && access(path, R_OK) != -1;\n}\n\nchar *swaynag_get_config_path(void) {\n\tstatic const char *config_paths[] = {\n\t\t\"$HOME/.swaynag/config\",\n\t\t\"$XDG_CONFIG_HOME/swaynag/config\",\n\t\tSYSCONFDIR \"/swaynag/config\",\n\t};\n\n\tchar *config_home = getenv(\"XDG_CONFIG_HOME\");\n\tif (!config_home || config_home[0] == '\\0') {\n\t\tconfig_paths[1] = \"$HOME/.config/swaynag/config\";\n\t}\n\n\twordexp_t p;\n\tfor (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) {\n\t\tif (wordexp(config_paths[i], &p, 0) == 0) {\n\t\t\tchar *path = strdup(p.we_wordv[0]);\n\t\t\twordfree(&p);\n\t\t\tif (file_exists(path)) {\n\t\t\t\treturn path;\n\t\t\t}\n\t\t\tfree(path);\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nint swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) {\n\tFILE *config = fopen(path, \"r\");\n\tif (!config) {\n\t\tfprintf(stderr, \"Failed to read config. Running without it.\\n\");\n\t\treturn 0;\n\t}\n\n\tstruct swaynag_type *type = swaynag_type_new(\"<config>\");\n\tlist_add(types, type);\n\n\tchar *line = NULL;\n\tsize_t line_size = 0;\n\tssize_t nread;\n\tint line_number = 0;\n\tint result = 0;\n\twhile ((nread = getline(&line, &line_size, config)) != -1) {\n\t\tline_number++;\n\t\tif (!*line || line[0] == '\\n' || line[0] == '#') {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (line[nread - 1] == '\\n') {\n\t\t\tline[nread - 1] = '\\0';\n\t\t}\n\n\t\tif (line[0] == '[') {\n\t\t\tchar *close = strchr(line, ']');\n\t\t\tif (!close || close != &line[nread - 2] || nread <= 3) {\n\t\t\t\tfprintf(stderr, \"Line %d is malformed\\n\", line_number);\n\t\t\t\tresult = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t*close = '\\0';\n\t\t\ttype = swaynag_type_get(types, &line[1]);\n\t\t\tif (!type) {\n\t\t\t\ttype = swaynag_type_new(&line[1]);\n\t\t\t\tlist_add(types, type);\n\t\t\t}\n\t\t} else {\n\t\t\tchar *flag = malloc(nread + 3);\n\t\t\tif (!flag) {\n\t\t\t\tperror(\"calloc\");\n\t\t\t\treturn EXIT_FAILURE;\n\t\t\t}\n\t\t\tsnprintf(flag, nread + 3, \"--%s\", line);\n\t\t\tchar *argv[] = {\"swaynag\", flag};\n\t\t\tresult = swaynag_parse_options(2, argv, swaynag, types, type,\n\t\t\t\t\tNULL, NULL);\n\t\t\tfree(flag);\n\t\t\tif (result != 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tfree(line);\n\tfclose(config);\n\treturn result;\n}\n"
  },
  {
    "path": "swaynag/main.c",
    "content": "#include <stdlib.h>\n#include <signal.h>\n#include \"log.h\"\n#include \"list.h\"\n#include \"swaynag/config.h\"\n#include \"swaynag/swaynag.h\"\n#include \"swaynag/types.h\"\n\nstatic struct swaynag swaynag;\n\nvoid sig_handler(int signal) {\n\tswaynag_destroy(&swaynag);\n\texit(EXIT_FAILURE);\n}\n\nint main(int argc, char **argv) {\n\tint status = EXIT_SUCCESS;\n\n\tlist_t *types = create_list();\n\tswaynag_types_add_default(types);\n\n\tswaynag.buttons = create_list();\n\twl_list_init(&swaynag.outputs);\n\twl_list_init(&swaynag.seats);\n\n\tstruct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button));\n\tbutton_close->text = strdup(\"X\");\n\tbutton_close->type = SWAYNAG_ACTION_DISMISS;\n\tlist_add(swaynag.buttons, button_close);\n\n\tswaynag.details.details_text = strdup(\"Toggle details\");\n\n\tchar *config_path = NULL;\n\tbool debug = false;\n\tstatus = swaynag_parse_options(argc, argv, NULL, NULL, NULL,\n\t\t\t&config_path, &debug);\n\tif (status != 0)  {\n\t\tgoto cleanup;\n\t}\n\tsway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL);\n\n\tif (!config_path) {\n\t\tconfig_path = swaynag_get_config_path();\n\t}\n\tif (config_path) {\n\t\tsway_log(SWAY_DEBUG, \"Loading config file: %s\", config_path);\n\t\tstatus = swaynag_load_config(config_path, &swaynag, types);\n\t\tfree(config_path);\n\t\tif (status != 0) {\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\n\tif (argc > 1) {\n\t\tstruct swaynag_type *type_args = swaynag_type_new(\"<args>\");\n\t\tlist_add(types, type_args);\n\n\t\tstatus = swaynag_parse_options(argc, argv, &swaynag, types,\n\t\t\t\ttype_args, NULL, NULL);\n\t\tif (status != 0) {\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\tif (!swaynag.message) {\n\t\tsway_log(SWAY_ERROR, \"No message passed. Please provide --message/-m\");\n\t\tstatus = EXIT_FAILURE;\n\t\tgoto cleanup;\n\t}\n\n\tif (!swaynag.type) {\n\t\tswaynag.type = swaynag_type_get(types, \"error\");\n\t}\n\n\t// Construct a new type with the defaults as the base, the general config\n\t// on top of that, followed by the type config, and finally any command\n\t// line arguments\n\tstruct swaynag_type *type = swaynag_type_new(swaynag.type->name);\n\tswaynag_type_merge(type, swaynag_type_get(types, \"<defaults>\"));\n\tswaynag_type_merge(type, swaynag_type_get(types, \"<config>\"));\n\tswaynag_type_merge(type, swaynag.type);\n\tswaynag_type_merge(type, swaynag_type_get(types, \"<args>\"));\n\tswaynag.type = type;\n\n\tif (swaynag.details.message) {\n\t\tswaynag.details.button_details = calloc(1, sizeof(struct swaynag_button));\n\t\tswaynag.details.button_details->text = strdup(swaynag.details.details_text);\n\t\tswaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND;\n\t\tlist_add(swaynag.buttons, swaynag.details.button_details);\n\t}\n\n\tsway_log(SWAY_DEBUG, \"Output: %s\", swaynag.type->output);\n\tsway_log(SWAY_DEBUG, \"Anchors: %\" PRIu32, swaynag.type->anchors);\n\tsway_log(SWAY_DEBUG, \"Type: %s\", swaynag.type->name);\n\tsway_log(SWAY_DEBUG, \"Message: %s\", swaynag.message);\n\tchar *font = pango_font_description_to_string(swaynag.type->font_description);\n\tsway_log(SWAY_DEBUG, \"Font: %s\", font);\n\tfree(font);\n\tsway_log(SWAY_DEBUG, \"Buttons\");\n\tfor (int i = 0; i < swaynag.buttons->length; i++) {\n\t\tstruct swaynag_button *button = swaynag.buttons->items[i];\n\t\tsway_log(SWAY_DEBUG, \"\\t[%s] `%s`\", button->text, button->action);\n\t}\n\n\tstruct sigaction sa = { .sa_handler = sig_handler };\n\tsigaction(SIGTERM, &sa, NULL);\n\n\tswaynag_setup(&swaynag);\n\tswaynag_run(&swaynag);\n\ncleanup:\n\tswaynag_types_free(types);\n\tswaynag_destroy(&swaynag);\n\treturn status;\n}\n"
  },
  {
    "path": "swaynag/meson.build",
    "content": "executable(\n\t'swaynag', [\n\t\t'config.c',\n\t\t'main.c',\n\t\t'render.c',\n\t\t'swaynag.c',\n\t\t'types.c',\n\t\twl_protos_src,\n\t],\n\tinclude_directories: [sway_inc],\n\tdependencies: [\n\t\tcairo,\n\t\tpango,\n\t\tpangocairo,\n\t\trt,\n\t\twayland_client,\n\t\twayland_cursor,\n\t],\n\tlink_with: [lib_sway_common, lib_sway_client],\n\tinstall: true\n)\n"
  },
  {
    "path": "swaynag/render.c",
    "content": "#include <stdint.h>\n#include \"cairo_util.h\"\n#include \"log.h\"\n#include \"pango.h\"\n#include \"pool-buffer.h\"\n#include \"swaynag/swaynag.h\"\n#include \"swaynag/types.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n\nstatic uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) {\n\tint text_width, text_height;\n\tget_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL,\n\t\t\t1, true, \"%s\", swaynag->message);\n\n\tint padding = swaynag->type->message_padding;\n\n\tuint32_t ideal_height = text_height + padding * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (swaynag->height < ideal_surface_height) {\n\t\treturn ideal_surface_height;\n\t}\n\n\tcairo_set_source_u32(cairo, swaynag->type->text);\n\tcairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2);\n\trender_text(cairo, swaynag->type->font_description, 1, false,\n\t\t\t\"%s\", swaynag->message);\n\n\treturn ideal_surface_height;\n}\n\nstatic void render_details_scroll_button(cairo_t *cairo,\n\t\tstruct swaynag *swaynag, struct swaynag_button *button) {\n\tint text_width, text_height;\n\tget_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL,\n\t\t\t1, true, \"%s\", button->text);\n\n\tint border = swaynag->type->button_border_thickness;\n\tint padding = swaynag->type->button_padding;\n\n\tcairo_set_source_u32(cairo, swaynag->type->details_background);\n\tcairo_rectangle(cairo, button->x, button->y,\n\t\t\tbutton->width, button->height);\n\tcairo_fill(cairo);\n\n\tcairo_set_source_u32(cairo, swaynag->type->button_background);\n\tcairo_rectangle(cairo, button->x + border, button->y + border,\n\t\t\tbutton->width - (border * 2), button->height - (border * 2));\n\tcairo_fill(cairo);\n\n\tcairo_set_source_u32(cairo, swaynag->type->button_text);\n\tcairo_move_to(cairo, button->x + border + padding,\n\t\t\tbutton->y + border + (button->height - text_height) / 2);\n\trender_text(cairo, swaynag->type->font_description, 1, true,\n\t\t\t\"%s\", button->text);\n}\n\nstatic int get_detailed_scroll_button_width(cairo_t *cairo,\n\t\tstruct swaynag *swaynag) {\n\tint up_width, down_width, temp_height;\n\tget_text_size(cairo, swaynag->type->font_description, &up_width, &temp_height, NULL,\n\t\t\t1, true,\n\t\t\t\"%s\", swaynag->details.button_up.text);\n\tget_text_size(cairo, swaynag->type->font_description, &down_width, &temp_height, NULL,\n\t\t\t1, true,\n\t\t\t\"%s\", swaynag->details.button_down.text);\n\n\tint text_width =  up_width > down_width ? up_width : down_width;\n\tint border = swaynag->type->button_border_thickness;\n\tint padding = swaynag->type->button_padding;\n\n\treturn text_width + border * 2 + padding * 2;\n}\n\nstatic uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag,\n\t\tuint32_t y) {\n\tuint32_t width = swaynag->width;\n\n\tint border = swaynag->type->details_border_thickness;\n\tint padding = swaynag->type->message_padding;\n\tint decor = padding + border;\n\n\tswaynag->details.x = decor;\n\tswaynag->details.y = y + decor;\n\tswaynag->details.width = width - decor * 2;\n\n\tPangoLayout *layout = get_pango_layout(cairo, swaynag->type->font_description,\n\t\t\tswaynag->details.message, 1, false);\n\tpango_layout_set_width(layout,\n\t\t\t(swaynag->details.width - padding * 2) * PANGO_SCALE);\n\tpango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);\n\tpango_layout_set_single_paragraph_mode(layout, false);\n\tpango_cairo_update_layout(cairo, layout);\n\tswaynag->details.total_lines = pango_layout_get_line_count(layout);\n\n\tPangoLayoutLine *line;\n\tline = pango_layout_get_line_readonly(layout, swaynag->details.offset);\n\tgint offset = line->start_index;\n\tconst char *text = pango_layout_get_text(layout);\n\tpango_layout_set_text(layout, text + offset, strlen(text) - offset);\n\n\tint text_width, text_height;\n\tpango_cairo_update_layout(cairo, layout);\n\tpango_layout_get_pixel_size(layout, &text_width, &text_height);\n\n\tbool show_buttons = swaynag->details.offset > 0;\n\tint button_width = get_detailed_scroll_button_width(cairo, swaynag);\n\tif (show_buttons) {\n\t\tswaynag->details.width -= button_width;\n\t\tpango_layout_set_width(layout,\n\t\t\t\t(swaynag->details.width - padding * 2) * PANGO_SCALE);\n\t}\n\n\tuint32_t ideal_height;\n\tdo {\n\t\tideal_height = swaynag->details.y + text_height + decor + padding * 2;\n\t\tif (ideal_height > SWAYNAG_MAX_HEIGHT) {\n\t\t\tideal_height = SWAYNAG_MAX_HEIGHT;\n\n\t\t\tif (!show_buttons) {\n\t\t\t\tshow_buttons = true;\n\t\t\t\tswaynag->details.width -= button_width;\n\t\t\t\tpango_layout_set_width(layout,\n\t\t\t\t\t\t(swaynag->details.width - padding * 2) * PANGO_SCALE);\n\t\t\t}\n\t\t}\n\n\t\tswaynag->details.height = ideal_height - swaynag->details.y - decor;\n\t\tpango_layout_set_height(layout,\n\t\t\t\t(swaynag->details.height - padding * 2) * PANGO_SCALE);\n\t\tpango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);\n\t\tpango_cairo_update_layout(cairo, layout);\n\t\tpango_layout_get_pixel_size(layout, &text_width, &text_height);\n\t} while (text_height != (swaynag->details.height - padding * 2));\n\n\tswaynag->details.visible_lines = pango_layout_get_line_count(layout);\n\n\tif (show_buttons) {\n\t\tswaynag->details.button_up.x =\n\t\t\tswaynag->details.x + swaynag->details.width;\n\t\tswaynag->details.button_up.y = swaynag->details.y;\n\t\tswaynag->details.button_up.width = button_width;\n\t\tswaynag->details.button_up.height = swaynag->details.height / 2;\n\t\trender_details_scroll_button(cairo, swaynag,\n\t\t\t\t&swaynag->details.button_up);\n\n\t\tswaynag->details.button_down.x =\n\t\t\tswaynag->details.x + swaynag->details.width;\n\t\tswaynag->details.button_down.y =\n\t\t\tswaynag->details.button_up.y + swaynag->details.button_up.height;\n\t\tswaynag->details.button_down.width = button_width;\n\t\tswaynag->details.button_down.height = swaynag->details.height / 2;\n\t\trender_details_scroll_button(cairo, swaynag,\n\t\t\t\t&swaynag->details.button_down);\n\t}\n\n\tcairo_set_source_u32(cairo, swaynag->type->details_background);\n\tcairo_rectangle(cairo, swaynag->details.x, swaynag->details.y,\n\t\t\tswaynag->details.width, swaynag->details.height);\n\tcairo_fill(cairo);\n\n\tcairo_move_to(cairo, swaynag->details.x + padding,\n\t\t\tswaynag->details.y + padding);\n\tcairo_set_source_u32(cairo, swaynag->type->text);\n\tpango_cairo_show_layout(cairo, layout);\n\tg_object_unref(layout);\n\n\treturn ideal_height;\n}\n\nstatic uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,\n\t\tint button_index, int *x) {\n\tstruct swaynag_button *button = swaynag->buttons->items[button_index];\n\n\tint text_width, text_height;\n\tget_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL,\n\t\t\t1, true, \"%s\", button->text);\n\n\tint border = swaynag->type->button_border_thickness;\n\tint padding = swaynag->type->button_padding;\n\n\tuint32_t ideal_height = text_height + padding * 2 + border * 2;\n\tuint32_t ideal_surface_height = ideal_height;\n\tif (swaynag->height < ideal_surface_height) {\n\t\treturn ideal_surface_height;\n\t}\n\n\tbutton->x = *x - border - text_width - padding * 2 + 1;\n\tbutton->y = (int)(ideal_height - text_height) / 2 - padding + 1;\n\tbutton->width = text_width + padding * 2;\n\tbutton->height = text_height + padding * 2;\n\n\tcairo_set_source_u32(cairo, swaynag->type->border);\n\tcairo_rectangle(cairo, button->x - border, button->y - border,\n\t\t\tbutton->width + border * 2, button->height + border * 2);\n\tcairo_fill(cairo);\n\n\tcairo_set_source_u32(cairo, swaynag->type->button_background);\n\tcairo_rectangle(cairo, button->x, button->y,\n\t\t\tbutton->width, button->height);\n\tcairo_fill(cairo);\n\n\tcairo_set_source_u32(cairo, swaynag->type->button_text);\n\tcairo_move_to(cairo, button->x + padding, button->y + padding);\n\trender_text(cairo, swaynag->type->font_description, 1, true,\n\t\t\t\"%s\", button->text);\n\n\t*x = button->x - border;\n\n\treturn ideal_surface_height;\n}\n\nstatic uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) {\n\tuint32_t max_height = 0;\n\n\tcairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);\n\tcairo_set_source_u32(cairo, swaynag->type->background);\n\tcairo_paint(cairo);\n\n\tuint32_t h = render_message(cairo, swaynag);\n\tmax_height = h > max_height ? h : max_height;\n\n\tint x = swaynag->width - swaynag->type->button_margin_right;\n\tfor (int i = 0; i < swaynag->buttons->length; i++) {\n\t\th = render_button(cairo, swaynag, i, &x);\n\t\tmax_height = h > max_height ? h : max_height;\n\t\tx -= swaynag->type->button_gap;\n\t\tif (i == 0) {\n\t\t\tx -= swaynag->type->button_gap_close;\n\t\t}\n\t}\n\n\tif (swaynag->details.visible) {\n\t\th = render_detailed(cairo, swaynag, max_height);\n\t\tmax_height = h > max_height ? h : max_height;\n\t}\n\n\tint border = swaynag->type->bar_border_thickness;\n\tif (max_height > swaynag->height) {\n\t\tmax_height += border;\n\t}\n\tcairo_set_source_u32(cairo, swaynag->type->border_bottom);\n\tcairo_rectangle(cairo, 0,\n\t\t\tswaynag->height - border,\n\t\t\tswaynag->width,\n\t\t\tborder);\n\tcairo_fill(cairo);\n\n\treturn max_height;\n}\n\nvoid render_frame(struct swaynag *swaynag) {\n\tif (!swaynag->run_display) {\n\t\treturn;\n\t}\n\n\tcairo_surface_t *recorder = cairo_recording_surface_create(\n\t\t\tCAIRO_CONTENT_COLOR_ALPHA, NULL);\n\tcairo_t *cairo = cairo_create(recorder);\n\tcairo_scale(cairo, swaynag->scale, swaynag->scale);\n\tcairo_save(cairo);\n\tcairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);\n\tcairo_paint(cairo);\n\tcairo_restore(cairo);\n\tuint32_t height = render_to_cairo(cairo, swaynag);\n\tif (height != swaynag->height) {\n\t\tzwlr_layer_surface_v1_set_size(swaynag->layer_surface, 0, height);\n\t\tzwlr_layer_surface_v1_set_exclusive_zone(swaynag->layer_surface,\n\t\t\t\theight);\n\t\twl_surface_commit(swaynag->surface);\n\t\twl_display_roundtrip(swaynag->display);\n\t} else {\n\t\tswaynag->current_buffer = get_next_buffer(swaynag->shm,\n\t\t\t\tswaynag->buffers,\n\t\t\t\tswaynag->width * swaynag->scale,\n\t\t\t\tswaynag->height * swaynag->scale);\n\t\tif (!swaynag->current_buffer) {\n\t\t\tsway_log(SWAY_DEBUG, \"Failed to get buffer. Skipping frame.\");\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\tcairo_t *shm = swaynag->current_buffer->cairo;\n\t\tcairo_save(shm);\n\t\tcairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);\n\t\tcairo_paint(shm);\n\t\tcairo_restore(shm);\n\t\tcairo_set_source_surface(shm, recorder, 0.0, 0.0);\n\t\tcairo_paint(shm);\n\n\t\twl_surface_set_buffer_scale(swaynag->surface, swaynag->scale);\n\t\twl_surface_attach(swaynag->surface,\n\t\t\t\tswaynag->current_buffer->buffer, 0, 0);\n\t\twl_surface_damage(swaynag->surface, 0, 0,\n\t\t\t\tswaynag->width, swaynag->height);\n\t\twl_surface_commit(swaynag->surface);\n\t\twl_display_roundtrip(swaynag->display);\n\t}\n\ncleanup:\n\tcairo_surface_destroy(recorder);\n\tcairo_destroy(cairo);\n}\n"
  },
  {
    "path": "swaynag/swaynag.1.scd",
    "content": "swaynag(1)\n\n# NAME\n\nswaynag - Show a warning or error message with buttons\n\n# SYNOPSIS\n\n_swaynag_ [options...]\n\n# OPTIONS\n\n*-b, --button* <text> <action>\n\tCreate a button with the text _text_ that executes _action_ when pressed.\n\tIf the environment variable _TERMINAL_ is set, _action_ will be run inside\n\tthe terminal. Otherwise, it will fallback to running directly. Multiple\n\tbuttons can be defined by providing the flag multiple times.\n\n*-B, --button-no-terminal* <text> <action>\n\tCreate a button with the text _text_ that executes _action_ when pressed.\n\t_action_ will be run directly instead of in a terminal. Multiple buttons\n\tcan be defined by providing the flag multiple times.\n\n*-z, --button-dismiss* <text> <action>\n\tCreate a button with the text _text_ that executes _action_ when pressed,\n\tand dismisses swaynag. If the environment variable _TERMINAL_ is set,\n\t_action_ will be run inside the terminal. Otherwise, it will fallback to\n\trunning directly. Multiple buttons can be defined by providing the flag\n\tmultiple times.\n\n*-Z, --button-dismiss-no-terminal* <text> <action>\n\tCreate a button with the text _text_ that executes _action_ when pressed,\n\tand dismisses swaynag. _action_ will be run directly instead of in a\n\tterminal. Multiple buttons can be defined by providing the flag multiple\n\ttimes.\n\n*-c, --config* <path>\n\tThe config file to use. By default, the following paths are checked:\n\t_$HOME/.swaynag/config_, _$XDG\\_CONFIG\\_HOME/swaynag/config_, and\n\t_SYSCONFDIR/swaynag/config_. All flags aside from this one and _debug_ are\n\tvalid options in the configuration file using the format\n\t_long-option=value_. All leading dashes should be omitted and the equals\n\tsign is required. See swaynag(5) for more information.\n\n*-d, --debug*\n\tEnable debugging.\n\n*-e, --edge* top|bottom\n\tSet the edge to use.\n\n*-y, --layer* overlay|top|bottom|background\n\tSet the layer to use.\n\n*-f, --font* <font>\n\tSet the font to use.\n\n*-h, --help*\n\tShow help message and quit.\n\n*-l, --detailed-message*\n\tRead a detailed message from stdin. A button to toggle details will be\n\tadded. Details are shown in a scrollable multi-line text area.\n\n*-L, --detailed-button* <text>\n\tSet the text for the button that toggles details. This has no effect if\n\tthere is not a detailed message. The default is _Toggle details_.\n\n*-m, --message* <msg>\n\tSet the message text.\n\n*-o, --output* <output>\n\tSet the output to use. This should be the name of a _xdg\\_output_.\n\n*-s, --dismiss-button* <text>\n\tSets the text for the dismiss nagbar button. The default is _X_.\n\n*-t, --type* <type>\n\tSet the message type. Two types are created by default _error_ and\n\t_warning_. Custom types can be defined in the config file. See\n\t_--config_ and swaynag(5) for details. Both of the default types can be\n\toverridden in the config file as well.\n\n*-v, --version*\n\tShow the version number and quit.\n\n# APPEARANCE OPTIONS\n\n*--background* <RRGGBB[AA]>\n\tSet the color of the background.\n\n*--border* <RRGGBB[AA]>\n\tSet the color of the border.\n\n*--border-bottom* <RRGGBB[AA]>\n\tSet the color of the bottom border.\n\n*--button-background* <RRGGBB[AA]>\n\tSet the color for the background for buttons.\n\n*--text* <RRGGBB[AA]>\n\tSet the text color.\n\n*--button-text* <RRGGBB[AA]>\n\tSet the button text color.\n\n*--border-bottom-size* <size>\n\tSet the thickness of the bottom border.\n\n*--message-padding* <padding>\n\tSet the padding for the message.\n\n*--details-background* <RRGGBB[AA]>\n\tSet the color for the background for details.\n\n*--details-border-size* <size>\n\tSet the thickness for the details border.\n\n*--button-border-size* <size>\n\tSet the thickness for the button border.\n\n*--button-gap* <gap>\n\tSet the size of the gap between buttons.\n\n*--button-dismiss-gap* <gap>\n\tSet the size of the gap between the dismiss button and another button.\n\n*--button-margin-right* <margin>\n\tSet the margin from the right of the dismiss button to edge.\n\n*--button-padding* <padding>\n\tSet the padding for the button text.\n\n# SEE\n\nswaynag(5)\n"
  },
  {
    "path": "swaynag/swaynag.5.scd",
    "content": "swaynag(5)\n\n# NAME\n\nswaynag - swaynag configuration file\n\n# SYNOPSIS\n\n$HOME/.swaynag/config, $XDG\\_CONFIG\\_HOME/swaynag/config,\nSYSCONFDIR/swaynag/config\n\n# CONFIG FILE\n\nAt the top of the config file, _swaynag_ options can be set using the format\n_long-option=value_. These will be used as default values if _swaynag_ is not\ngiven the option. This can be useful for setting a preferred font, output, and\nedge.\n\nBelow the options, custom types may be defined. To define a type, use the\nfollowing format:\n\n```\n[name-of-type]\noption=value\n```\n\nAll colors may be given in the form _RRGGBB_ or _RRGGBBAA_. The following\ncolors can be set:\n\n*background=<color>*\n\tThe background color for _swaynag_.\n\n*border=<color>*\n\tThe color to use for borders of buttons.\n\n*border-bottom=<color>*\n\tThe color of the border line at the bottom of _swaynag_.\n\n*button-background=<color>*\n\tThe background color for the buttons.\n\n*text=<color>*\n\tThe color of the text.\n\n*button-text=<color>*\n\tThe color of the button text.\n\nThe following sizing options can also be set:\n\n*border-bottom-size=<size>*\n\tSet the thickness of the bottom border.\n\n*message-padding=<padding>*\n\tSet the padding for the message.\n\n*details-background=<color>*\n\tThe background color for the details.\n\n*details-border-size=<size>*\n\tSet the thickness for the details border.\n\n*button-border-size=<size>*\n\tSet the thickness for the button border.\n\n*button-gap=<gap>*\n\tSet the size of the gap between buttons.\n\n*button-dismiss-gap=<gap>*\n\tSet the size of the gap between the dismiss button and another button.\n\n*button-margin-right=<margin>*\n\tSet the margin from the right of the dismiss button to edge.\n\n*button-padding=<padding>*\n\tSet the padding for the button text.\n\nAdditionally, the following options can be assigned a default per-type:\n\n*edge=top|bottom*\n\tSet the edge to use.\n\n*layer=overlay|top|bottom|background*\n\tSet the layer to use.\n\n*font=<font>*\n\tSet the font to use.\n\n*output=<output>*\n\tSet the output to use. This should be the name of a _xdg\\_output_.\n\n# EXAMPLE\n\n```\nfont=Monospace 12\nedge=bottom\n\n[green]\nedge=top\nbackground=00AA00\nborder=006600\nborder-bottom=004400\ntext=FFFFFF\nbutton-background=00CC00\nmessage-padding=10\n```\n\n# SEE\n\nswaynag(1)\n"
  },
  {
    "path": "swaynag/swaynag.c",
    "content": "#include <stdlib.h>\n#include <assert.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <wayland-client.h>\n#include <wayland-cursor.h>\n#include \"log.h\"\n#include \"list.h\"\n#include \"swaynag/render.h\"\n#include \"swaynag/swaynag.h\"\n#include \"swaynag/types.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n\nstatic void nop() {\n\t// Intentionally left blank\n}\n\nstatic bool terminal_execute(char *terminal, char *command) {\n\tchar fname[] = \"/tmp/swaynagXXXXXX\";\n\tFILE *tmp= fdopen(mkstemp(fname), \"w\");\n\tif (!tmp) {\n\t\tsway_log(SWAY_ERROR, \"Failed to create temp script\");\n\t\treturn false;\n\t}\n\tsway_log(SWAY_DEBUG, \"Created temp script: %s\", fname);\n\tfprintf(tmp, \"#!/bin/sh\\nrm %s\\n%s\", fname, command);\n\tfclose(tmp);\n\tchmod(fname, S_IRUSR | S_IWUSR | S_IXUSR);\n\tsize_t cmd_size = strlen(terminal) + strlen(\" -e \") + strlen(fname) + 1;\n\tchar *cmd = malloc(cmd_size);\n\tif (!cmd) {\n\t\tperror(\"malloc\");\n\t\treturn false;\n\t}\n\tsnprintf(cmd, cmd_size, \"%s -e %s\", terminal, fname);\n\texeclp(\"sh\", \"sh\", \"-c\", cmd, NULL);\n\tsway_log_errno(SWAY_ERROR, \"Failed to run command, execlp() returned.\");\n\tfree(cmd);\n\treturn false;\n}\n\nstatic void swaynag_button_execute(struct swaynag *swaynag,\n\t\tstruct swaynag_button *button) {\n\tsway_log(SWAY_DEBUG, \"Executing [%s]: %s\", button->text, button->action);\n\tif (button->type == SWAYNAG_ACTION_DISMISS) {\n\t\tswaynag->run_display = false;\n\t} else if (button->type == SWAYNAG_ACTION_EXPAND) {\n\t\tswaynag->details.visible = !swaynag->details.visible;\n\t\trender_frame(swaynag);\n\t} else {\n\t\tpid_t pid = fork();\n\t\tif (pid < 0) {\n\t\t\tsway_log_errno(SWAY_DEBUG, \"Failed to fork\");\n\t\t\treturn;\n\t\t} else if (pid == 0) {\n\t\t\t// Child process. Will be used to prevent zombie processes\n\t\t\tpid = fork();\n\t\t\tif (pid < 0) {\n\t\t\t\tsway_log_errno(SWAY_DEBUG, \"Failed to fork\");\n\t\t\t\treturn;\n\t\t\t} else if (pid == 0) {\n\t\t\t\t// Child of the child. Will be reparented to the init process\n\t\t\t\tchar *terminal = getenv(\"TERMINAL\");\n\t\t\t\tif (button->terminal && terminal && *terminal) {\n\t\t\t\t\tsway_log(SWAY_DEBUG, \"Found $TERMINAL: %s\", terminal);\n\t\t\t\t\tif (!terminal_execute(terminal, button->action)) {\n\t\t\t\t\t\tswaynag_destroy(swaynag);\n\t\t\t\t\t\t_exit(EXIT_FAILURE);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (button->terminal) {\n\t\t\t\t\t\tsway_log(SWAY_DEBUG,\n\t\t\t\t\t\t\t\t\"$TERMINAL not found. Running directly\");\n\t\t\t\t\t}\n\t\t\t\t\texeclp(\"sh\", \"sh\", \"-c\", button->action, NULL);\n\t\t\t\t\tsway_log_errno(SWAY_DEBUG, \"execlp failed\");\n\t\t\t\t\t_exit(EXIT_FAILURE);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_exit(EXIT_SUCCESS);\n\t\t}\n\n\t\tif (button->dismiss) {\n\t\t\tswaynag->run_display = false;\n\t\t}\n\n\t\tif (waitpid(pid, NULL, 0) < 0) {\n\t\t\tsway_log_errno(SWAY_DEBUG, \"waitpid failed\");\n\t\t}\n\t}\n}\n\nstatic void layer_surface_configure(void *data,\n\t\tstruct zwlr_layer_surface_v1 *surface,\n\t\tuint32_t serial, uint32_t width, uint32_t height) {\n\tstruct swaynag *swaynag = data;\n\tswaynag->width = width;\n\tswaynag->height = height;\n\tzwlr_layer_surface_v1_ack_configure(surface, serial);\n\trender_frame(swaynag);\n}\n\nstatic void layer_surface_closed(void *data,\n\t\tstruct zwlr_layer_surface_v1 *surface) {\n\tstruct swaynag *swaynag = data;\n\tswaynag_destroy(swaynag);\n}\n\nstatic const struct zwlr_layer_surface_v1_listener layer_surface_listener = {\n\t.configure = layer_surface_configure,\n\t.closed = layer_surface_closed,\n};\n\nstatic void surface_enter(void *data, struct wl_surface *surface,\n\t\tstruct wl_output *output) {\n\tstruct swaynag *swaynag = data;\n\tstruct swaynag_output *swaynag_output;\n\twl_list_for_each(swaynag_output, &swaynag->outputs, link) {\n\t\tif (swaynag_output->wl_output == output) {\n\t\t\tsway_log(SWAY_DEBUG, \"Surface enter on output %s\",\n\t\t\t\t\tswaynag_output->name);\n\t\t\tswaynag->output = swaynag_output;\n\t\t\tswaynag->scale = swaynag->output->scale;\n\t\t\trender_frame(swaynag);\n\t\t\tbreak;\n\t\t}\n\t};\n}\n\nstatic const struct wl_surface_listener surface_listener = {\n\t.enter = surface_enter,\n\t.leave = nop,\n};\n\nstatic void update_cursor(struct swaynag_seat *seat) {\n\tstruct swaynag_pointer *pointer = &seat->pointer;\n\tstruct swaynag *swaynag = seat->swaynag;\n\tif (pointer->cursor_theme) {\n\t\twl_cursor_theme_destroy(pointer->cursor_theme);\n\t}\n\tconst char *cursor_theme = getenv(\"XCURSOR_THEME\");\n\tunsigned cursor_size = 24;\n\tconst char *env_cursor_size = getenv(\"XCURSOR_SIZE\");\n\tif (env_cursor_size && *env_cursor_size) {\n\t\terrno = 0;\n\t\tchar *end;\n\t\tunsigned size = strtoul(env_cursor_size, &end, 10);\n\t\tif (!*end && errno == 0) {\n\t\t\tcursor_size = size;\n\t\t}\n\t}\n\tpointer->cursor_theme = wl_cursor_theme_load(\n\t\tcursor_theme, cursor_size * swaynag->scale, swaynag->shm);\n\tif (!pointer->cursor_theme) {\n\t\tsway_log(SWAY_ERROR, \"Failed to load cursor theme\");\n\t\treturn;\n\t}\n\tstruct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, \"default\");\n\tif (!cursor) {\n\t\tsway_log(SWAY_ERROR, \"Failed to get default cursor from theme\");\n\t\treturn;\n\t}\n\tpointer->cursor_image = cursor->images[0];\n\twl_surface_set_buffer_scale(pointer->cursor_surface,\n\t\t\tswaynag->scale);\n\twl_surface_attach(pointer->cursor_surface,\n\t\t\twl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);\n\twl_pointer_set_cursor(pointer->pointer, pointer->serial,\n\t\t\tpointer->cursor_surface,\n\t\t\tpointer->cursor_image->hotspot_x / swaynag->scale,\n\t\t\tpointer->cursor_image->hotspot_y / swaynag->scale);\n\twl_surface_damage_buffer(pointer->cursor_surface, 0, 0,\n\t\t\tINT32_MAX, INT32_MAX);\n\twl_surface_commit(pointer->cursor_surface);\n}\n\nvoid update_all_cursors(struct swaynag *swaynag) {\n\tstruct swaynag_seat *seat;\n\twl_list_for_each(seat, &swaynag->seats, link) {\n\t\tif (seat->pointer.pointer) {\n\t\t\tupdate_cursor(seat);\n\t\t}\n\t}\n}\n\nstatic void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t serial, struct wl_surface *surface,\n\t\twl_fixed_t surface_x, wl_fixed_t surface_y) {\n\tstruct swaynag_seat *seat = data;\n\n\tstruct swaynag_pointer *pointer = &seat->pointer;\n\tpointer->x = wl_fixed_to_int(surface_x);\n\tpointer->y = wl_fixed_to_int(surface_y);\n\n\tif (seat->swaynag->cursor_shape_manager) {\n\t\tstruct wp_cursor_shape_device_v1 *device =\n\t\t\twp_cursor_shape_manager_v1_get_pointer(\n\t\t\t\tseat->swaynag->cursor_shape_manager, wl_pointer);\n\t\twp_cursor_shape_device_v1_set_shape(device, serial,\n\t\t\tWP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);\n\t\twp_cursor_shape_device_v1_destroy(device);\n\t} else {\n\t\tpointer->serial = serial;\n\t\tupdate_cursor(seat);\n\t}\n}\n\nstatic void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {\n\tstruct swaynag_seat *seat = data;\n\tseat->pointer.x = wl_fixed_to_int(surface_x);\n\tseat->pointer.y = wl_fixed_to_int(surface_y);\n}\n\nstatic void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t serial, uint32_t time, uint32_t button, uint32_t state) {\n\tstruct swaynag_seat *seat = data;\n\tstruct swaynag *swaynag = seat->swaynag;\n\n\tif (state != WL_POINTER_BUTTON_STATE_PRESSED) {\n\t\treturn;\n\t}\n\n\tdouble x = seat->pointer.x;\n\tdouble y = seat->pointer.y;\n\tfor (int i = 0; i < swaynag->buttons->length; i++) {\n\t\tstruct swaynag_button *nagbutton = swaynag->buttons->items[i];\n\t\tif (x >= nagbutton->x\n\t\t\t\t&& y >= nagbutton->y\n\t\t\t\t&& x < nagbutton->x + nagbutton->width\n\t\t\t\t&& y < nagbutton->y + nagbutton->height) {\n\t\t\tswaynag_button_execute(swaynag, nagbutton);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (swaynag->details.visible &&\n\t\t\tswaynag->details.total_lines != swaynag->details.visible_lines) {\n\t\tstruct swaynag_button button_up = swaynag->details.button_up;\n\t\tif (x >= button_up.x\n\t\t\t\t&& y >= button_up.y\n\t\t\t\t&& x < button_up.x + button_up.width\n\t\t\t\t&& y < button_up.y + button_up.height\n\t\t\t\t&& swaynag->details.offset > 0) {\n\t\t\tswaynag->details.offset--;\n\t\t\trender_frame(swaynag);\n\t\t\treturn;\n\t\t}\n\n\t\tstruct swaynag_button button_down = swaynag->details.button_down;\n\t\tint bot = swaynag->details.total_lines;\n\t\tbot -= swaynag->details.visible_lines;\n\t\tif (x >= button_down.x\n\t\t\t\t&& y >= button_down.y\n\t\t\t\t&& x < button_down.x + button_down.width\n\t\t\t\t&& y < button_down.y + button_down.height\n\t\t\t\t&& swaynag->details.offset < bot) {\n\t\t\tswaynag->details.offset++;\n\t\t\trender_frame(swaynag);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,\n\t\tuint32_t time, uint32_t axis, wl_fixed_t value) {\n\tstruct swaynag_seat *seat = data;\n\tstruct swaynag *swaynag = seat->swaynag;\n\tif (!swaynag->details.visible\n\t\t\t|| seat->pointer.x < swaynag->details.x\n\t\t\t|| seat->pointer.y < swaynag->details.y\n\t\t\t|| seat->pointer.x >= swaynag->details.x + swaynag->details.width\n\t\t\t|| seat->pointer.y >= swaynag->details.y + swaynag->details.height\n\t\t\t|| swaynag->details.total_lines == swaynag->details.visible_lines) {\n\t\treturn;\n\t}\n\n\tint direction = wl_fixed_to_int(value);\n\tint bot = swaynag->details.total_lines - swaynag->details.visible_lines;\n\tif (direction < 0 && swaynag->details.offset > 0) {\n\t\tswaynag->details.offset--;\n\t} else if (direction > 0 && swaynag->details.offset < bot) {\n\t\tswaynag->details.offset++;\n\t}\n\n\trender_frame(swaynag);\n}\n\nstatic const struct wl_pointer_listener pointer_listener = {\n\t.enter = wl_pointer_enter,\n\t.leave = nop,\n\t.motion = wl_pointer_motion,\n\t.button = wl_pointer_button,\n\t.axis = wl_pointer_axis,\n\t.frame = nop,\n\t.axis_source = nop,\n\t.axis_stop = nop,\n\t.axis_discrete = nop,\n};\n\nstatic void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,\n\t\tenum wl_seat_capability caps) {\n\tstruct swaynag_seat *seat = data;\n\tbool cap_pointer = caps & WL_SEAT_CAPABILITY_POINTER;\n\tif (cap_pointer && !seat->pointer.pointer) {\n\t\tseat->pointer.pointer = wl_seat_get_pointer(wl_seat);\n\t\twl_pointer_add_listener(seat->pointer.pointer,\n\t\t\t\t&pointer_listener, seat);\n\t} else if (!cap_pointer && seat->pointer.pointer) {\n\t\twl_pointer_destroy(seat->pointer.pointer);\n\t\tseat->pointer.pointer = NULL;\n\t}\n}\n\nstatic const struct wl_seat_listener seat_listener = {\n\t.capabilities = seat_handle_capabilities,\n\t.name = nop,\n};\n\nstatic void output_scale(void *data, struct wl_output *output,\n\t\tint32_t factor) {\n\tstruct swaynag_output *swaynag_output = data;\n\tswaynag_output->scale = factor;\n\tif (swaynag_output->swaynag->output == swaynag_output) {\n\t\tswaynag_output->swaynag->scale = swaynag_output->scale;\n\t\tif (!swaynag_output->swaynag->cursor_shape_manager) {\n\t\t\tupdate_all_cursors(swaynag_output->swaynag);\n\t\t}\n\t\trender_frame(swaynag_output->swaynag);\n\t}\n}\n\nstatic void output_name(void *data, struct wl_output *output,\n\t\tconst char *name) {\n\tstruct swaynag_output *swaynag_output = data;\n\tswaynag_output->name = strdup(name);\n\n\tconst char *outname = swaynag_output->swaynag->type->output;\n\tif (!swaynag_output->swaynag->output && outname &&\n\t\t\tstrcmp(outname, name) == 0) {\n\t\tsway_log(SWAY_DEBUG, \"Using output %s\", name);\n\t\tswaynag_output->swaynag->output = swaynag_output;\n\t}\n}\n\nstatic const struct wl_output_listener output_listener = {\n\t.geometry = nop,\n\t.mode = nop,\n\t.done = nop,\n\t.scale = output_scale,\n\t.name = output_name,\n\t.description = nop,\n};\n\nstatic void handle_global(void *data, struct wl_registry *registry,\n\t\tuint32_t name, const char *interface, uint32_t version) {\n\tstruct swaynag *swaynag = data;\n\tif (strcmp(interface, wl_compositor_interface.name) == 0) {\n\t\tswaynag->compositor = wl_registry_bind(registry, name,\n\t\t\t\t&wl_compositor_interface, 4);\n\t} else if (strcmp(interface, wl_seat_interface.name) == 0) {\n\t\tstruct swaynag_seat *seat =\n\t\t\tcalloc(1, sizeof(struct swaynag_seat));\n\t\tif (!seat) {\n\t\t\tperror(\"calloc\");\n\t\t\treturn;\n\t\t}\n\n\t\tseat->swaynag = swaynag;\n\t\tseat->wl_name = name;\n\t\tseat->wl_seat =\n\t\t\twl_registry_bind(registry, name, &wl_seat_interface, 1);\n\n\t\twl_seat_add_listener(seat->wl_seat, &seat_listener, seat);\n\n\t\twl_list_insert(&swaynag->seats, &seat->link);\n\t} else if (strcmp(interface, wl_shm_interface.name) == 0) {\n\t\tswaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);\n\t} else if (strcmp(interface, wl_output_interface.name) == 0) {\n\t\tif (!swaynag->output) {\n\t\t\tstruct swaynag_output *output =\n\t\t\t\tcalloc(1, sizeof(struct swaynag_output));\n\t\t\tif (!output) {\n\t\t\t\tperror(\"calloc\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toutput->wl_output = wl_registry_bind(registry, name,\n\t\t\t\t\t&wl_output_interface, 4);\n\t\t\toutput->wl_name = name;\n\t\t\toutput->scale = 1;\n\t\t\toutput->swaynag = swaynag;\n\t\t\twl_list_insert(&swaynag->outputs, &output->link);\n\t\t\twl_output_add_listener(output->wl_output,\n\t\t\t\t\t&output_listener, output);\n\t\t}\n\t} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {\n\t\tswaynag->layer_shell = wl_registry_bind(\n\t\t\t\tregistry, name, &zwlr_layer_shell_v1_interface, 1);\n\t} else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {\n\t\tswaynag->cursor_shape_manager = wl_registry_bind(\n\t\t\t\tregistry, name, &wp_cursor_shape_manager_v1_interface, 1);\n\t}\n}\n\nvoid swaynag_seat_destroy(struct swaynag_seat *seat) {\n\tif (seat->pointer.cursor_theme) {\n\t\twl_cursor_theme_destroy(seat->pointer.cursor_theme);\n\t}\n\tif (seat->pointer.pointer) {\n\t\twl_pointer_destroy(seat->pointer.pointer);\n\t}\n\twl_seat_destroy(seat->wl_seat);\n\twl_list_remove(&seat->link);\n\tfree(seat);\n}\n\nstatic void handle_global_remove(void *data, struct wl_registry *registry,\n\t\tuint32_t name) {\n\tstruct swaynag *swaynag = data;\n\tif (swaynag->output->wl_name == name) {\n\t\tswaynag->run_display = false;\n\t}\n\n\tstruct swaynag_seat *seat, *tmpseat;\n\twl_list_for_each_safe(seat, tmpseat, &swaynag->seats, link) {\n\t\tif (seat->wl_name == name) {\n\t\t\tswaynag_seat_destroy(seat);\n\t\t}\n\t}\n}\n\nstatic const struct wl_registry_listener registry_listener = {\n\t.global = handle_global,\n\t.global_remove = handle_global_remove,\n};\n\nvoid swaynag_setup_cursors(struct swaynag *swaynag) {\n\tstruct swaynag_seat *seat;\n\n\twl_list_for_each(seat, &swaynag->seats, link) {\n\t\tstruct swaynag_pointer *p = &seat->pointer;\n\n\t\tp->cursor_surface =\n\t\t\twl_compositor_create_surface(swaynag->compositor);\n\t\tassert(p->cursor_surface);\n\t}\n}\n\nvoid swaynag_setup(struct swaynag *swaynag) {\n\tswaynag->display = wl_display_connect(NULL);\n\tif (!swaynag->display) {\n\t\tsway_abort(\"Unable to connect to the compositor. \"\n\t\t\t\t\"If your compositor is running, check or set the \"\n\t\t\t\t\"WAYLAND_DISPLAY environment variable.\");\n\t}\n\n\tswaynag->scale = 1;\n\n\tstruct wl_registry *registry = wl_display_get_registry(swaynag->display);\n\twl_registry_add_listener(registry, &registry_listener, swaynag);\n\tif (wl_display_roundtrip(swaynag->display) < 0) {\n\t\tsway_abort(\"failed to register with the wayland display\");\n\t}\n\n\tassert(swaynag->compositor && swaynag->layer_shell && swaynag->shm);\n\n\t// Second roundtrip to get wl_output properties\n\tif (wl_display_roundtrip(swaynag->display) < 0) {\n\t\tsway_log(SWAY_ERROR, \"Error during outputs init.\");\n\t\tswaynag_destroy(swaynag);\n\t\texit(EXIT_FAILURE);\n\t}\n\n\tif (!swaynag->output && swaynag->type->output) {\n\t\tsway_log(SWAY_ERROR, \"Output '%s' not found\", swaynag->type->output);\n\t\tswaynag_destroy(swaynag);\n\t\texit(EXIT_FAILURE);\n\t}\n\n\tif (!swaynag->cursor_shape_manager) {\n\t\tswaynag_setup_cursors(swaynag);\n\t}\n\n\tswaynag->surface = wl_compositor_create_surface(swaynag->compositor);\n\tassert(swaynag->surface);\n\twl_surface_add_listener(swaynag->surface, &surface_listener, swaynag);\n\n\tswaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface(\n\t\t\tswaynag->layer_shell, swaynag->surface,\n\t\t\tswaynag->output ? swaynag->output->wl_output : NULL,\n\t\t\tswaynag->type->layer,\n\t\t\t\"swaynag\");\n\tassert(swaynag->layer_surface);\n\tzwlr_layer_surface_v1_add_listener(swaynag->layer_surface,\n\t\t\t&layer_surface_listener, swaynag);\n\tzwlr_layer_surface_v1_set_anchor(swaynag->layer_surface,\n\t\t\tswaynag->type->anchors);\n\n\twl_registry_destroy(registry);\n}\n\nvoid swaynag_run(struct swaynag *swaynag) {\n\tswaynag->run_display = true;\n\trender_frame(swaynag);\n\twhile (swaynag->run_display\n\t\t\t&& wl_display_dispatch(swaynag->display) != -1) {\n\t\t// This is intentionally left blank\n\t}\n}\n\nvoid swaynag_destroy(struct swaynag *swaynag) {\n\tswaynag->run_display = false;\n\n\tfree(swaynag->message);\n\tfor (int i = 0; i < swaynag->buttons->length; ++i) {\n\t\tstruct swaynag_button *button = swaynag->buttons->items[i];\n\t\tfree(button->text);\n\t\tfree(button->action);\n\t\tfree(button);\n\t}\n\tlist_free(swaynag->buttons);\n\tfree(swaynag->details.message);\n\tfree(swaynag->details.details_text);\n\tfree(swaynag->details.button_up.text);\n\tfree(swaynag->details.button_down.text);\n\n\tif (swaynag->type) {\n\t\tswaynag_type_free(swaynag->type);\n\t}\n\n\tif (swaynag->layer_surface) {\n\t\tzwlr_layer_surface_v1_destroy(swaynag->layer_surface);\n\t}\n\n\tif (swaynag->surface) {\n\t\twl_surface_destroy(swaynag->surface);\n\t}\n\n\tstruct swaynag_seat *seat, *tmpseat;\n\twl_list_for_each_safe(seat, tmpseat, &swaynag->seats, link) {\n\t\tswaynag_seat_destroy(seat);\n\t}\n\n\tdestroy_buffer(&swaynag->buffers[0]);\n\tdestroy_buffer(&swaynag->buffers[1]);\n\n\tif (swaynag->outputs.prev || swaynag->outputs.next) {\n\t\tstruct swaynag_output *output, *temp;\n\t\twl_list_for_each_safe(output, temp, &swaynag->outputs, link) {\n\t\t\twl_output_destroy(output->wl_output);\n\t\t\tfree(output->name);\n\t\t\twl_list_remove(&output->link);\n\t\t\tfree(output);\n\t\t};\n\t}\n\n\tif (swaynag->compositor) {\n\t\twl_compositor_destroy(swaynag->compositor);\n\t}\n\n\tif (swaynag->shm) {\n\t\twl_shm_destroy(swaynag->shm);\n\t}\n\n\tif (swaynag->display) {\n\t\twl_display_disconnect(swaynag->display);\n\t}\n}\n"
  },
  {
    "path": "swaynag/types.c",
    "content": "#include <getopt.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <strings.h>\n#include \"list.h\"\n#include \"log.h\"\n#include \"swaynag/config.h\"\n#include \"swaynag/types.h\"\n#include \"util.h\"\n#include \"wlr-layer-shell-unstable-v1-client-protocol.h\"\n\nstruct swaynag_type *swaynag_type_new(const char *name) {\n\tstruct swaynag_type *type = calloc(1, sizeof(struct swaynag_type));\n\tif (!type) {\n\t\tsway_abort(\"Failed to allocate type: %s\", name);\n\t}\n\ttype->name = strdup(name);\n\ttype->bar_border_thickness = -1;\n\ttype->message_padding = -1;\n\ttype->details_border_thickness = -1;\n\ttype->button_border_thickness = -1;\n\ttype->button_gap = -1;\n\ttype->button_gap_close = -1;\n\ttype->button_margin_right = -1;\n\ttype->button_padding = -1;\n\ttype->layer = -1;\n\treturn type;\n}\n\nvoid swaynag_types_add_default(list_t *types) {\n\tstruct swaynag_type *type_defaults = swaynag_type_new(\"<defaults>\");\n\ttype_defaults->font_description =\n\t\tpango_font_description_from_string(\"pango:Monospace 10\");\n\ttype_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP\n\t\t| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT\n\t\t| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;\n\ttype_defaults->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;\n\ttype_defaults->button_background = 0x333333FF;\n\ttype_defaults->details_background = 0x333333FF;\n\ttype_defaults->background = 0x323232FF;\n\ttype_defaults->text = 0xFFFFFFFF;\n\ttype_defaults->button_text = 0xFFFFFFFF;\n\ttype_defaults->border = 0x222222FF;\n\ttype_defaults->border_bottom = 0x444444FF;\n\ttype_defaults->bar_border_thickness = 2;\n\ttype_defaults->message_padding = 8;\n\ttype_defaults->details_border_thickness = 3;\n\ttype_defaults->button_border_thickness = 3;\n\ttype_defaults->button_gap = 20;\n\ttype_defaults->button_gap_close = 15;\n\ttype_defaults->button_margin_right = 2;\n\ttype_defaults->button_padding = 3;\n\tlist_add(types, type_defaults);\n\n\tstruct swaynag_type *type_error = swaynag_type_new(\"error\");\n\ttype_error->button_background = 0x680A0AFF;\n\ttype_error->details_background = 0x680A0AFF;\n\ttype_error->background = 0x900000FF;\n\ttype_error->text = 0xFFFFFFFF;\n\ttype_error->button_text = 0xFFFFFFFF;\n\ttype_error->border = 0xD92424FF;\n\ttype_error->border_bottom = 0x470909FF;\n\tlist_add(types, type_error);\n\n\tstruct swaynag_type *type_warning = swaynag_type_new(\"warning\");\n\ttype_warning->button_background = 0xFFC100FF;\n\ttype_warning->details_background = 0xFFC100FF;\n\ttype_warning->background = 0xFFA800FF;\n\ttype_warning->text = 0x000000FF;\n\ttype_warning->button_text = 0x000000FF;\n\ttype_warning->border = 0xAB7100FF;\n\ttype_warning->border_bottom = 0xAB7100FF;\n\tlist_add(types, type_warning);\n}\n\nstruct swaynag_type *swaynag_type_get(list_t *types, char *name) {\n\tfor (int i = 0; i < types->length; i++) {\n\t\tstruct swaynag_type *type = types->items[i];\n\t\tif (strcasecmp(type->name, name) == 0) {\n\t\t\treturn type;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nvoid swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) {\n\tif (!dest || !src) {\n\t\treturn;\n\t}\n\n\tif (src->font_description) {\n\t\tdest->font_description = pango_font_description_copy(src->font_description);\n\t}\n\n\tif (src->output) {\n\t\tdest->output = strdup(src->output);\n\t}\n\n\tif (src->anchors > 0) {\n\t\tdest->anchors = src->anchors;\n\t}\n\n\tif (src->layer >= 0) {\n\t\tdest->layer = src->layer;\n\t}\n\n\t// Colors\n\tif (src->button_background > 0) {\n\t\tdest->button_background = src->button_background;\n\t}\n\n\tif (src->details_background > 0) {\n\t\tdest->details_background = src->details_background;\n\t}\n\n\tif (src->background > 0) {\n\t\tdest->background = src->background;\n\t}\n\n\tif (src->text > 0) {\n\t\tdest->text = src->text;\n\t}\n\n\tif (src->button_text > 0) {\n\t\tdest->button_text = src->button_text;\n\t}\n\n\n\tif (src->border > 0) {\n\t\tdest->border = src->border;\n\t}\n\n\tif (src->border_bottom > 0) {\n\t\tdest->border_bottom = src->border_bottom;\n\t}\n\n\t// Sizing\n\tif (src->bar_border_thickness > -1) {\n\t\tdest->bar_border_thickness = src->bar_border_thickness;\n\t}\n\n\tif (src->message_padding > -1) {\n\t\tdest->message_padding = src->message_padding;\n\t}\n\n\tif (src->details_border_thickness > -1) {\n\t\tdest->details_border_thickness = src->details_border_thickness;\n\t}\n\n\tif (src->button_border_thickness > -1) {\n\t\tdest->button_border_thickness = src->button_border_thickness;\n\t}\n\n\tif (src->button_gap > -1) {\n\t\tdest->button_gap = src->button_gap;\n\t}\n\n\tif (src->button_gap_close > -1) {\n\t\tdest->button_gap_close = src->button_gap_close;\n\t}\n\n\tif (src->button_margin_right > -1) {\n\t\tdest->button_margin_right = src->button_margin_right;\n\t}\n\n\tif (src->button_padding > -1) {\n\t\tdest->button_padding = src->button_padding;\n\t}\n}\n\nvoid swaynag_type_free(struct swaynag_type *type) {\n\tfree(type->name);\n\tpango_font_description_free(type->font_description);\n\tfree(type->output);\n\tfree(type);\n}\n\nvoid swaynag_types_free(list_t *types) {\n\tfor (int i = 0; i < types->length; ++i) {\n\t\tswaynag_type_free(types->items[i]);\n\t}\n\tlist_free(types);\n}\n"
  }
]